Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cb2af3dc3 | ||
|
|
9d999f92a9 | ||
|
|
01e8668d1a | ||
|
|
8e58d962a7 | ||
|
|
c59c308d32 | ||
|
|
4560f2cca9 | ||
|
|
872752b820 | ||
|
|
97bb2b69c5 | ||
|
|
08fe87c8af | ||
|
|
d43e5fcefc | ||
|
|
65492a108c | ||
|
|
1939ff4153 | ||
|
|
4c32248d86 | ||
|
|
5ddbfdf44b | ||
|
|
7e86c20383 | ||
|
|
3adf77617b | ||
|
|
1bc62a5fb4 | ||
|
|
d473a211d0 | ||
|
|
3aa239e58d | ||
|
|
223aa5e70b | ||
|
|
5eef60a4eb | ||
|
|
de750995cb | ||
|
|
0f58fbcebd | ||
|
|
6dc964c425 | ||
|
|
4171c08413 | ||
|
|
4348ab45dc | ||
|
|
df46e3a0e5 | ||
|
|
2772101bbf | ||
|
|
72fee51d32 | ||
|
|
ffe0f98360 | ||
|
|
68377a84bc | ||
|
|
50d4c399e9 | ||
|
|
5a3955dfcb | ||
|
|
03227b52c0 | ||
|
|
8a8fbccfaa | ||
|
|
018d9a7b8d | ||
|
|
774fd566d1 | ||
|
|
b81fcae6fb | ||
|
|
b6b34c9062 | ||
|
|
f73baa091a | ||
|
|
a838cb7ad4 | ||
|
|
2afcade4f2 | ||
|
|
d70333baa6 | ||
|
|
43cfebd97a |
@@ -12,11 +12,6 @@ RUN curl -L https://fly.io/install.sh | sh
|
|||||||
ENV FLYCTL_INSTALL="/root/.fly"
|
ENV FLYCTL_INSTALL="/root/.fly"
|
||||||
ENV PATH="$FLYCTL_INSTALL/bin:$PATH"
|
ENV PATH="$FLYCTL_INSTALL/bin:$PATH"
|
||||||
|
|
||||||
RUN sudo apt-get update
|
|
||||||
RUN sudo apt-get install python3-pip python3 -y
|
|
||||||
|
|
||||||
RUN pip3 install modal
|
|
||||||
|
|
||||||
# RUN echo 'export FLYCTL_INSTALL="/home/node/.fly"' >> ~/.bashrc
|
# RUN echo 'export FLYCTL_INSTALL="/home/node/.fly"' >> ~/.bashrc
|
||||||
# RUN echo 'export PATH="$FLYCTL_INSTALL/bin:$PATH"' >> ~/.bashrc
|
# RUN echo 'export PATH="$FLYCTL_INSTALL/bin:$PATH"' >> ~/.bashrc
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@
|
|||||||
"stivo.tailwind-fold",
|
"stivo.tailwind-fold",
|
||||||
"streetsidesoftware.code-spell-checker",
|
"streetsidesoftware.code-spell-checker",
|
||||||
"GitHub.copilot",
|
"GitHub.copilot",
|
||||||
"ms-azuretools.vscode-docker",
|
"ms-azuretools.vscode-docker"
|
||||||
"ms-python.python"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vercel
|
|
||||||
@@ -75,10 +75,11 @@ Major areas
|
|||||||
3. `bun i`
|
3. `bun i`
|
||||||
4. Start docker
|
4. Start docker
|
||||||
5. `cp .env.example .env.local`
|
5. `cp .env.example .env.local`
|
||||||
6. Repace `JWT_SECRET` with `openssl rand -hex 32`
|
6. Replace `JWT_SECRET` with `openssl rand -hex 32`
|
||||||
7. Get a local clerk dev key for `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY`
|
7. Get a local clerk dev key for `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY`
|
||||||
8. Keep a terminal live for `bun run db-dev`
|
8. Keep a terminal live for `bun run db-dev`
|
||||||
9. Finally start the next server with `bun dev`
|
9. Execute the local migration to create the initial data `bun run migrate-local`
|
||||||
|
10. Finally start the next server with `bun dev`
|
||||||
|
|
||||||
**Schema Changes**
|
**Schema Changes**
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
MODAL_TOKEN_ID=
|
MODAL_TOKEN_ID=
|
||||||
MODAL_TOKEN_SECRET=
|
MODAL_TOKEN_SECRET=
|
||||||
CIVITAI_API_KEY=
|
|
||||||
|
|
||||||
# On production set to False
|
# On production set to False
|
||||||
DEPLOY_TEST_FLAG=True
|
DEPLOY_TEST_FLAG=True
|
||||||
CIVITAI_API_KEY=
|
|
||||||
PUBLIC_MODEL_VOLUME_NAME=
|
|
||||||
@@ -42,9 +42,6 @@ fly secrets set MODAL_TOKEN_SECRET=
|
|||||||
|
|
||||||
## To deploy
|
## To deploy
|
||||||
|
|
||||||
We have 2 `toml` files one for `production` and the other for `staging`
|
|
||||||
it will default to `staging` if you don't include the `-c` flag to choose your config file
|
|
||||||
|
|
||||||
```
|
```
|
||||||
// model-builder/fly.toml
|
// model-builder/fly.toml
|
||||||
app = <APP_NAME>
|
app = <APP_NAME>
|
||||||
@@ -56,5 +53,5 @@ fly launch
|
|||||||
```
|
```
|
||||||
if not, run this instead
|
if not, run this instead
|
||||||
```
|
```
|
||||||
fly deploy -c "toml file"
|
fly deploy
|
||||||
```
|
```
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# fly.toml app configuration file generated for modal-builder on 2024-01-03T22:29:34+08:00
|
|
||||||
#
|
|
||||||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
|
||||||
#
|
|
||||||
|
|
||||||
app = "modal-builder-production"
|
|
||||||
primary_region = "sea"
|
|
||||||
|
|
||||||
[build]
|
|
||||||
|
|
||||||
[http_service]
|
|
||||||
internal_port = 8080
|
|
||||||
force_https = true
|
|
||||||
auto_stop_machines = true
|
|
||||||
auto_start_machines = true
|
|
||||||
min_machines_running = 0
|
|
||||||
processes = ["app"]
|
|
||||||
|
|
||||||
[[vm]]
|
|
||||||
cpu_kind = 'shared'
|
|
||||||
cpus = 1
|
|
||||||
memory_mb = 1024
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# fly.toml app configuration file generated for modal-builder-preview on 2024-01-25T09:26:31Z
|
# fly.toml app configuration file generated for modal-builder on 2024-01-03T22:29:34+08:00
|
||||||
#
|
#
|
||||||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||||
#
|
#
|
||||||
|
|
||||||
app = 'modal-builder-preview'
|
app = "modal-builder"
|
||||||
primary_region = 'sea'
|
primary_region = "sea"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
|
|
||||||
@@ -14,9 +14,4 @@ primary_region = 'sea'
|
|||||||
auto_stop_machines = true
|
auto_stop_machines = true
|
||||||
auto_start_machines = true
|
auto_start_machines = true
|
||||||
min_machines_running = 0
|
min_machines_running = 0
|
||||||
processes = ['app']
|
processes = ["app"]
|
||||||
|
|
||||||
[[vm]]
|
|
||||||
cpu_kind = 'shared'
|
|
||||||
cpus = 1
|
|
||||||
memory_mb = 1024
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from enum import Enum
|
|||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
from uuid import uuid4
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
@@ -20,7 +19,6 @@ from urllib.parse import parse_qs
|
|||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
from starlette.types import ASGIApp, Scope, Receive, Send
|
from starlette.types import ASGIApp, Scope, Receive, Send
|
||||||
|
|
||||||
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
# executor = ThreadPoolExecutor(max_workers=5)
|
# executor = ThreadPoolExecutor(max_workers=5)
|
||||||
@@ -47,8 +45,6 @@ machine_id_websocket_dict = {}
|
|||||||
machine_id_status = {}
|
machine_id_status = {}
|
||||||
|
|
||||||
fly_instance_id = os.environ.get('FLY_ALLOC_ID', 'local').split('-')[0]
|
fly_instance_id = os.environ.get('FLY_ALLOC_ID', 'local').split('-')[0]
|
||||||
civitai_api_key = os.environ.get('FLY_ALLOC_ID', 'local')
|
|
||||||
public_model_volume_name = os.environ.get('PUBLIC_MODEL_VOLUME_NAME', 'local')
|
|
||||||
|
|
||||||
|
|
||||||
class FlyReplayMiddleware(BaseHTTPMiddleware):
|
class FlyReplayMiddleware(BaseHTTPMiddleware):
|
||||||
@@ -143,7 +139,6 @@ def read_root():
|
|||||||
class GitCustomNodes(BaseModel):
|
class GitCustomNodes(BaseModel):
|
||||||
hash: str
|
hash: str
|
||||||
disabled: bool
|
disabled: bool
|
||||||
pip: Optional[List[str]] = None
|
|
||||||
|
|
||||||
class FileCustomNodes(BaseModel):
|
class FileCustomNodes(BaseModel):
|
||||||
filename: str
|
filename: str
|
||||||
@@ -179,7 +174,6 @@ class Item(BaseModel):
|
|||||||
snapshot: Snapshot
|
snapshot: Snapshot
|
||||||
models: List[Model]
|
models: List[Model]
|
||||||
callback_url: str
|
callback_url: str
|
||||||
model_volume_name: str
|
|
||||||
gpu: GPUType = Field(default=GPUType.T4)
|
gpu: GPUType = Field(default=GPUType.T4)
|
||||||
|
|
||||||
@field_validator('gpu')
|
@field_validator('gpu')
|
||||||
@@ -229,71 +223,6 @@ async def websocket_endpoint(websocket: WebSocket, machine_id: str):
|
|||||||
|
|
||||||
# return {"Hello": "World"}
|
# return {"Hello": "World"}
|
||||||
|
|
||||||
# definition based on web schema
|
|
||||||
class UploadType(str, Enum):
|
|
||||||
checkpoint = "checkpoint"
|
|
||||||
lora = "lora"
|
|
||||||
embedding = "embedding"
|
|
||||||
|
|
||||||
class UploadBody(BaseModel):
|
|
||||||
download_url: str
|
|
||||||
volume_name: str
|
|
||||||
volume_id: str
|
|
||||||
model_id: str
|
|
||||||
upload_type: UploadType
|
|
||||||
callback_url: str
|
|
||||||
|
|
||||||
|
|
||||||
# based on ComfyUI's model dir, and our mappings in ./src/template/data/extra_model_paths.yaml
|
|
||||||
UPLOAD_TYPE_DIR_MAP = {
|
|
||||||
UploadType.checkpoint: "checkpoints",
|
|
||||||
UploadType.lora: "loras",
|
|
||||||
UploadType.embedding: "embeddings",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/upload-volume")
|
|
||||||
async def upload_model(body: UploadBody):
|
|
||||||
global last_activity_time
|
|
||||||
last_activity_time = time.time()
|
|
||||||
logger.info(f"Extended inactivity time to {global_timeout}")
|
|
||||||
|
|
||||||
asyncio.create_task(upload_logic(body))
|
|
||||||
|
|
||||||
# check that this
|
|
||||||
return JSONResponse(status_code=200, content={"message": "Volume uploading", "build_machine_instance_id": fly_instance_id})
|
|
||||||
|
|
||||||
|
|
||||||
async def upload_logic(body: UploadBody):
|
|
||||||
folder_path = f"/app/builds/{body.volume_id}"
|
|
||||||
|
|
||||||
cp_process = await asyncio.subprocess.create_subprocess_exec("cp", "-r", "/app/src/volume-builder", folder_path)
|
|
||||||
await cp_process.wait()
|
|
||||||
|
|
||||||
upload_path = UPLOAD_TYPE_DIR_MAP[body.upload_type]
|
|
||||||
config = {
|
|
||||||
"volume_names": {
|
|
||||||
body.volume_name: {"download_url": body.download_url, "folder_path": upload_path}
|
|
||||||
},
|
|
||||||
"volume_paths": {
|
|
||||||
body.volume_name: f'/volumes/{uuid4()}'
|
|
||||||
},
|
|
||||||
"callback_url": body.callback_url,
|
|
||||||
"callback_body": {
|
|
||||||
"model_id": body.model_id,
|
|
||||||
"volume_id": body.volume_id,
|
|
||||||
"folder_path": upload_path,
|
|
||||||
},
|
|
||||||
"civitai_api_key": os.environ.get('CIVITAI_API_KEY')
|
|
||||||
}
|
|
||||||
with open(f"{folder_path}/config.py", "w") as f:
|
|
||||||
f.write("config = " + json.dumps(config))
|
|
||||||
|
|
||||||
await asyncio.subprocess.create_subprocess_shell(
|
|
||||||
f"modal run app.py",
|
|
||||||
cwd=folder_path,
|
|
||||||
env={**os.environ, "COLUMNS": "10000"}
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.post("/create")
|
@app.post("/create")
|
||||||
async def create_machine(item: Item):
|
async def create_machine(item: Item):
|
||||||
@@ -379,19 +308,12 @@ async def build_logic(item: Item):
|
|||||||
cp_process = await asyncio.subprocess.create_subprocess_exec("cp", "-r", "/app/src/template", folder_path)
|
cp_process = await asyncio.subprocess.create_subprocess_exec("cp", "-r", "/app/src/template", folder_path)
|
||||||
await cp_process.wait()
|
await cp_process.wait()
|
||||||
|
|
||||||
pip_modules = set()
|
|
||||||
for git_custom_node in item.snapshot.git_custom_nodes.values():
|
|
||||||
if git_custom_node.pip:
|
|
||||||
pip_modules.update(git_custom_node.pip)
|
|
||||||
|
|
||||||
# Write the config file
|
# Write the config file
|
||||||
config = {
|
config = {
|
||||||
"name": item.name,
|
"name": item.name,
|
||||||
"deploy_test": os.environ.get("DEPLOY_TEST_FLAG", "False"),
|
"deploy_test": os.environ.get("DEPLOY_TEST_FLAG", "False"),
|
||||||
"gpu": item.gpu,
|
"gpu": item.gpu,
|
||||||
"public_model_volume": public_model_volume_name,
|
"civitai_token": os.environ.get("CIVITAI_TOKEN", "")
|
||||||
"private_model_volume": item.model_volume_name,
|
|
||||||
"pip": list(pip_modules)
|
|
||||||
}
|
}
|
||||||
with open(f"{folder_path}/config.py", "w") as f:
|
with open(f"{folder_path}/config.py", "w") as f:
|
||||||
f.write("config = " + json.dumps(config))
|
f.write("config = " + json.dumps(config))
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
from config import config
|
from config import config
|
||||||
import modal
|
import modal
|
||||||
from modal import Image, Mount, web_endpoint, Stub, asgi_app, method, enter, exit
|
from modal import Image, Mount, web_endpoint, Stub, asgi_app
|
||||||
import json
|
import json
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from fastapi import FastAPI, Request, HTTPException
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from volume_setup import volumes
|
|
||||||
from datetime import datetime
|
|
||||||
# deploy_test = False
|
# deploy_test = False
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -28,8 +27,8 @@ deploy_test = config["deploy_test"] == "True"
|
|||||||
web_app = FastAPI()
|
web_app = FastAPI()
|
||||||
print(config)
|
print(config)
|
||||||
print("deploy_test ", deploy_test)
|
print("deploy_test ", deploy_test)
|
||||||
print('volumes', volumes)
|
|
||||||
stub = Stub(name=config["name"])
|
stub = Stub(name=config["name"])
|
||||||
|
# print(stub.app_id)
|
||||||
|
|
||||||
if not deploy_test:
|
if not deploy_test:
|
||||||
# dockerfile_image = Image.from_dockerfile(f"{current_directory}/Dockerfile", context_mount=Mount.from_local_dir(f"{current_directory}/data", remote_path="/data"))
|
# dockerfile_image = Image.from_dockerfile(f"{current_directory}/Dockerfile", context_mount=Mount.from_local_dir(f"{current_directory}/data", remote_path="/data"))
|
||||||
@@ -37,6 +36,9 @@ if not deploy_test:
|
|||||||
|
|
||||||
dockerfile_image = (
|
dockerfile_image = (
|
||||||
modal.Image.debian_slim()
|
modal.Image.debian_slim()
|
||||||
|
.env({
|
||||||
|
"CIVITAI_TOKEN": config["civitai_token"],
|
||||||
|
})
|
||||||
.apt_install("git", "wget")
|
.apt_install("git", "wget")
|
||||||
.pip_install(
|
.pip_install(
|
||||||
"git+https://github.com/modal-labs/asgiproxy.git", "httpx", "tqdm"
|
"git+https://github.com/modal-labs/asgiproxy.git", "httpx", "tqdm"
|
||||||
@@ -53,20 +55,16 @@ if not deploy_test:
|
|||||||
"cd /comfyui/custom_nodes/ComfyUI-Manager && pip install -r requirements.txt",
|
"cd /comfyui/custom_nodes/ComfyUI-Manager && pip install -r requirements.txt",
|
||||||
"cd /comfyui/custom_nodes/ComfyUI-Manager && mkdir startup-scripts",
|
"cd /comfyui/custom_nodes/ComfyUI-Manager && mkdir startup-scripts",
|
||||||
)
|
)
|
||||||
.run_commands(f"cat /comfyui/server.py")
|
|
||||||
.run_commands(f"ls /comfyui/app")
|
|
||||||
# .run_commands(
|
# .run_commands(
|
||||||
# # Install comfy deploy
|
# # Install comfy deploy
|
||||||
# "cd /comfyui/custom_nodes && git clone https://github.com/BennyKok/comfyui-deploy.git",
|
# "cd /comfyui/custom_nodes && git clone https://github.com/BennyKok/comfyui-deploy.git",
|
||||||
# )
|
# )
|
||||||
.copy_local_file(f"{current_directory}/data/extra_model_paths.yaml", "/comfyui")
|
# .copy_local_file(f"{current_directory}/data/extra_model_paths.yaml", "/comfyui")
|
||||||
|
|
||||||
.copy_local_file(f"{current_directory}/data/start.sh", "/start.sh")
|
.copy_local_file(f"{current_directory}/data/start.sh", "/start.sh")
|
||||||
.run_commands("chmod +x /start.sh")
|
.run_commands("chmod +x /start.sh")
|
||||||
|
|
||||||
# Restore the custom nodes first
|
# Restore the custom nodes first
|
||||||
.pip_install(config["pip"])
|
|
||||||
|
|
||||||
.copy_local_file(f"{current_directory}/data/restore_snapshot.py", "/")
|
.copy_local_file(f"{current_directory}/data/restore_snapshot.py", "/")
|
||||||
.copy_local_file(f"{current_directory}/data/snapshot.json", "/comfyui/custom_nodes/ComfyUI-Manager/startup-scripts/restore-snapshot.json")
|
.copy_local_file(f"{current_directory}/data/snapshot.json", "/comfyui/custom_nodes/ComfyUI-Manager/startup-scripts/restore-snapshot.json")
|
||||||
.run_commands("python restore_snapshot.py")
|
.run_commands("python restore_snapshot.py")
|
||||||
@@ -158,110 +156,90 @@ image = Image.debian_slim()
|
|||||||
|
|
||||||
target_image = image if deploy_test else dockerfile_image
|
target_image = image if deploy_test else dockerfile_image
|
||||||
|
|
||||||
@stub.cls(image=target_image, gpu=config["gpu"] ,volumes=volumes, timeout=60 * 10, container_idle_timeout=60 * 5)
|
|
||||||
class ComfyDeployRunner:
|
|
||||||
|
|
||||||
@enter()
|
@stub.function(image=target_image, gpu=config["gpu"])
|
||||||
def setup(self):
|
def run(input: Input):
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
# Make sure that the ComfyUI API is available
|
# Make sure that the ComfyUI API is available
|
||||||
print(f"comfy-modal - check server")
|
print(f"comfy-modal - check server")
|
||||||
|
|
||||||
command = ["python", "main.py",
|
command = ["python", "main.py",
|
||||||
"--disable-auto-launch", "--disable-metadata"]
|
"--disable-auto-launch", "--disable-metadata"]
|
||||||
|
server_process = subprocess.Popen(command, cwd="/comfyui")
|
||||||
|
|
||||||
self.server_process = subprocess.Popen(command, cwd="/comfyui")
|
check_server(
|
||||||
|
f"http://{COMFY_HOST}",
|
||||||
|
COMFY_API_AVAILABLE_MAX_RETRIES,
|
||||||
|
COMFY_API_AVAILABLE_INTERVAL_MS,
|
||||||
|
)
|
||||||
|
|
||||||
check_server(
|
job_input = input
|
||||||
f"http://{COMFY_HOST}",
|
|
||||||
COMFY_API_AVAILABLE_MAX_RETRIES,
|
|
||||||
COMFY_API_AVAILABLE_INTERVAL_MS,
|
|
||||||
)
|
|
||||||
|
|
||||||
@exit()
|
# print(f"comfy-modal - got input {job_input}")
|
||||||
def cleanup(self, exc_type, exc_value, traceback):
|
|
||||||
self.server_process.terminate()
|
|
||||||
|
|
||||||
@method()
|
# Queue the workflow
|
||||||
def run(self, input: Input):
|
try:
|
||||||
data = json.dumps({
|
# job_input is the json input
|
||||||
"run_id": input.prompt_id,
|
queued_workflow = queue_workflow_comfy_deploy(
|
||||||
"status": "started",
|
job_input) # queue_workflow(workflow)
|
||||||
"time": datetime.now().isoformat()
|
prompt_id = queued_workflow["prompt_id"]
|
||||||
}).encode('utf-8')
|
print(f"comfy-modal - queued workflow with ID {prompt_id}")
|
||||||
req = urllib.request.Request(input.status_endpoint, data=data, method='POST')
|
except Exception as e:
|
||||||
urllib.request.urlopen(req)
|
import traceback
|
||||||
|
print(traceback.format_exc())
|
||||||
|
return {"error": f"Error queuing workflow: {str(e)}"}
|
||||||
|
|
||||||
job_input = input
|
# Poll for completion
|
||||||
|
print(f"comfy-modal - wait until image generation is complete")
|
||||||
|
retries = 0
|
||||||
|
status = ""
|
||||||
|
try:
|
||||||
|
print("getting request")
|
||||||
|
while retries < COMFY_POLLING_MAX_RETRIES:
|
||||||
|
status_result = check_status(prompt_id=prompt_id)
|
||||||
|
# history = get_history(prompt_id)
|
||||||
|
|
||||||
try:
|
# Exit the loop if we have found the history
|
||||||
queued_workflow = queue_workflow_comfy_deploy(job_input) # queue_workflow(workflow)
|
# if prompt_id in history and history[prompt_id].get("outputs"):
|
||||||
prompt_id = queued_workflow["prompt_id"]
|
# break
|
||||||
print(f"comfy-modal - queued workflow with ID {prompt_id}")
|
|
||||||
except Exception as e:
|
|
||||||
import traceback
|
|
||||||
print(traceback.format_exc())
|
|
||||||
return {"error": f"Error queuing workflow: {str(e)}"}
|
|
||||||
|
|
||||||
# Poll for completion
|
# Exit the loop if we have found the status both success or failed
|
||||||
print(f"comfy-modal - wait until image generation is complete")
|
if 'status' in status_result and (status_result['status'] == 'success' or status_result['status'] == 'failed'):
|
||||||
retries = 0
|
status = status_result['status']
|
||||||
status = ""
|
print(status)
|
||||||
try:
|
break
|
||||||
print("getting request")
|
|
||||||
while retries < COMFY_POLLING_MAX_RETRIES:
|
|
||||||
status_result = check_status(prompt_id=prompt_id)
|
|
||||||
# history = get_history(prompt_id)
|
|
||||||
|
|
||||||
# Exit the loop if we have found the history
|
|
||||||
# if prompt_id in history and history[prompt_id].get("outputs"):
|
|
||||||
# break
|
|
||||||
|
|
||||||
# Exit the loop if we have found the status both success or failed
|
|
||||||
if 'status' in status_result and (status_result['status'] == 'success' or status_result['status'] == 'failed'):
|
|
||||||
status = status_result['status']
|
|
||||||
print(status)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Wait before trying again
|
|
||||||
time.sleep(COMFY_POLLING_INTERVAL_MS / 1000)
|
|
||||||
retries += 1
|
|
||||||
else:
|
else:
|
||||||
return {"error": "Max retries reached while waiting for image generation"}
|
# Wait before trying again
|
||||||
except Exception as e:
|
time.sleep(COMFY_POLLING_INTERVAL_MS / 1000)
|
||||||
return {"error": f"Error waiting for image generation: {str(e)}"}
|
retries += 1
|
||||||
|
else:
|
||||||
|
return {"error": "Max retries reached while waiting for image generation"}
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": f"Error waiting for image generation: {str(e)}"}
|
||||||
|
|
||||||
print(f"comfy-modal - Finished, turning off")
|
print(f"comfy-modal - Finished, turning off")
|
||||||
|
server_process.terminate()
|
||||||
|
|
||||||
result = {"status": status}
|
# Get the generated image and return it as URL in an AWS bucket or as base64
|
||||||
|
# images_result = process_output_images(history[prompt_id].get("outputs"), job["id"])
|
||||||
|
# result = {**images_result, "refresh_worker": REFRESH_WORKER}
|
||||||
|
result = {"status": status}
|
||||||
|
|
||||||
|
return result
|
||||||
|
print("Running remotely on Modal!")
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@web_app.post("/run")
|
@web_app.post("/run")
|
||||||
async def post_run(request_input: RequestInput):
|
async def bar(request_input: RequestInput):
|
||||||
|
# print(request_input)
|
||||||
if not deploy_test:
|
if not deploy_test:
|
||||||
# print(request_input.input.prompt_id, request_input.input.status_endpoint)
|
run.spawn(request_input.input)
|
||||||
data = json.dumps({
|
return {"status": "success"}
|
||||||
"run_id": request_input.input.prompt_id,
|
# pass
|
||||||
"status": "queued",
|
|
||||||
"time": datetime.now().isoformat()
|
|
||||||
}).encode('utf-8')
|
|
||||||
req = urllib.request.Request(request_input.input.status_endpoint, data=data, method='POST')
|
|
||||||
urllib.request.urlopen(req)
|
|
||||||
|
|
||||||
model = ComfyDeployRunner()
|
|
||||||
call = model.run.spawn(request_input.input)
|
|
||||||
|
|
||||||
# call = run.spawn()
|
@stub.function(image=image)
|
||||||
return {"call_id": call.object_id}
|
|
||||||
|
|
||||||
return {"call_id": None}
|
|
||||||
|
|
||||||
@stub.function(image=image
|
|
||||||
,volumes=volumes
|
|
||||||
)
|
|
||||||
@asgi_app()
|
@asgi_app()
|
||||||
def comfyui_api():
|
def comfyui_api():
|
||||||
return web_app
|
return web_app
|
||||||
@@ -311,7 +289,6 @@ def spawn_comfyui_in_background():
|
|||||||
# to be on a single container.
|
# to be on a single container.
|
||||||
concurrency_limit=1,
|
concurrency_limit=1,
|
||||||
timeout=10 * 60,
|
timeout=10 * 60,
|
||||||
volumes=volumes,
|
|
||||||
)
|
)
|
||||||
@asgi_app()
|
@asgi_app()
|
||||||
def comfyui_app():
|
def comfyui_app():
|
||||||
@@ -330,4 +307,4 @@ def comfyui_app():
|
|||||||
},
|
},
|
||||||
)()
|
)()
|
||||||
|
|
||||||
return make_simple_proxy_app(ProxyContext(config))
|
return make_simple_proxy_app(ProxyContext(config))
|
||||||
@@ -1,8 +1 @@
|
|||||||
config = {
|
config = {"name": "my-app", "deploy_test": "True", "gpu": "T4"}
|
||||||
"name": "my-app",
|
|
||||||
"deploy_test": "False",
|
|
||||||
"gpu": "T4",
|
|
||||||
"public_model_volume": "model-store",
|
|
||||||
"private_model_volume": "private-model-store",
|
|
||||||
"pip": []
|
|
||||||
}
|
|
||||||
@@ -1,23 +1,11 @@
|
|||||||
public:
|
comfyui:
|
||||||
base_path: /public_models/
|
base_path: /runpod-volume/ComfyUI/
|
||||||
checkpoints: checkpoints
|
checkpoints: models/checkpoints/
|
||||||
clip: clip
|
clip: models/clip/
|
||||||
clip_vision: clip_vision
|
clip_vision: models/clip_vision/
|
||||||
configs: configs
|
configs: models/configs/
|
||||||
controlnet: controlnet
|
controlnet: models/controlnet/
|
||||||
embeddings: embeddings
|
embeddings: models/embeddings/
|
||||||
loras: loras
|
loras: models/loras/
|
||||||
upscale_models: upscale_models
|
upscale_models: models/upscale_models/
|
||||||
vae: vae
|
vae: models/vae/
|
||||||
|
|
||||||
private:
|
|
||||||
base_path: /private_models/
|
|
||||||
checkpoints: checkpoints
|
|
||||||
clip: clip
|
|
||||||
clip_vision: clip_vision
|
|
||||||
configs: configs
|
|
||||||
controlnet: controlnet
|
|
||||||
embeddings: embeddings
|
|
||||||
loras: loras
|
|
||||||
upscale_models: upscale_models
|
|
||||||
vae: vae
|
|
||||||
@@ -49,9 +49,15 @@ with open('models.json') as f:
|
|||||||
models = json.load(f)
|
models = json.load(f)
|
||||||
|
|
||||||
for model in models:
|
for model in models:
|
||||||
|
import os
|
||||||
|
if "civitai.com/api" in model['url'] and not "token=" in model['url']:
|
||||||
|
if "?" in model['url']:
|
||||||
|
model['url'] += "&token=" + os.environ.get('CIVITAI_TOKEN', '')
|
||||||
|
else:
|
||||||
|
model['url'] += "?token=" + os.environ.get('CIVITAI_TOKEN', '')
|
||||||
response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers)
|
response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers)
|
||||||
print(response.text)
|
print(response.text)
|
||||||
|
|
||||||
# Close the server
|
# Close the server
|
||||||
server_process.terminate()
|
server_process.terminate()
|
||||||
print("Finished installing dependencies.")
|
print("Finished installing dependencies.")
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
{
|
{
|
||||||
"comfyui": "d0165d819afe76bd4e6bdd710eb5f3e571b6a804",
|
"comfyui": "d0165d819afe76bd4e6bdd710eb5f3e571b6a804",
|
||||||
"git_custom_nodes": {
|
"git_custom_nodes": {},
|
||||||
"https://github.com/BennyKok/comfyui-deploy.git": {
|
"file_custom_nodes": []
|
||||||
"hash": "a838cb7ad425e5652c3931fbafdc886b53c48a22",
|
}
|
||||||
"disabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"file_custom_nodes": []
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import modal
|
|
||||||
from config import config
|
|
||||||
|
|
||||||
public_model_volume = modal.Volume.persisted(config["public_model_volume"])
|
|
||||||
private_volume = modal.Volume.persisted(config["private_model_volume"])
|
|
||||||
|
|
||||||
PUBLIC_BASEMODEL_DIR = "/public_models"
|
|
||||||
PRIVATE_BASEMODEL_DIR = "/private_models"
|
|
||||||
volumes = {PUBLIC_BASEMODEL_DIR: public_model_volume, PRIVATE_BASEMODEL_DIR: private_volume}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import modal
|
|
||||||
from config import config
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
stub = modal.Stub()
|
|
||||||
|
|
||||||
# Volume names may only contain alphanumeric characters, dashes, periods, and underscores, and must be less than 64 characters in length.
|
|
||||||
def is_valid_name(name: str) -> bool:
|
|
||||||
allowed_characters = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._")
|
|
||||||
return 0 < len(name) <= 64 and all(char in allowed_characters for char in name)
|
|
||||||
|
|
||||||
def create_volumes(volume_names, paths):
|
|
||||||
path_to_vol = {}
|
|
||||||
for volume_name in volume_names.keys():
|
|
||||||
if not is_valid_name(volume_name):
|
|
||||||
pass
|
|
||||||
modal_volume = modal.Volume.persisted(volume_name)
|
|
||||||
path_to_vol[paths[volume_name]] = modal_volume
|
|
||||||
|
|
||||||
return path_to_vol
|
|
||||||
|
|
||||||
vol_name_to_links = config["volume_names"]
|
|
||||||
vol_name_to_path = config["volume_paths"]
|
|
||||||
callback_url = config["callback_url"]
|
|
||||||
callback_body = config["callback_body"]
|
|
||||||
civitai_key = config["civitai_api_key"]
|
|
||||||
|
|
||||||
volumes = create_volumes(vol_name_to_links, vol_name_to_path)
|
|
||||||
image = (
|
|
||||||
modal.Image.debian_slim().apt_install("wget").pip_install("requests")
|
|
||||||
)
|
|
||||||
|
|
||||||
# download config { "download_url": "", "folder_path": ""}
|
|
||||||
timeout=5000
|
|
||||||
@stub.function(volumes=volumes, image=image, timeout=timeout, gpu=None)
|
|
||||||
def download_model(volume_name, download_config):
|
|
||||||
import requests
|
|
||||||
download_url = download_config["download_url"]
|
|
||||||
folder_path = download_config["folder_path"]
|
|
||||||
|
|
||||||
volume_base_path = vol_name_to_path[volume_name]
|
|
||||||
model_store_path = os.path.join(volume_base_path, folder_path)
|
|
||||||
modified_download_url = download_url + ("&" if "?" in download_url else "?") + "token=" + civitai_key # civitai requires auth
|
|
||||||
print('downloading', modified_download_url)
|
|
||||||
|
|
||||||
subprocess.run(["wget", modified_download_url , "--content-disposition", "-P", model_store_path])
|
|
||||||
subprocess.run(["ls", "-la", volume_base_path])
|
|
||||||
subprocess.run(["ls", "-la", model_store_path])
|
|
||||||
volumes[volume_base_path].commit()
|
|
||||||
|
|
||||||
|
|
||||||
status = {"status": "success"}
|
|
||||||
requests.post(callback_url, json={**status, **callback_body})
|
|
||||||
print(f"finished! sending to {callback_url}")
|
|
||||||
pprint({**status, **callback_body})
|
|
||||||
|
|
||||||
@stub.local_entrypoint()
|
|
||||||
def simple_download():
|
|
||||||
import requests
|
|
||||||
try:
|
|
||||||
list(download_model.starmap([(vol_name, link) for vol_name,link in vol_name_to_links.items()]))
|
|
||||||
except modal.exception.FunctionTimeoutError as e:
|
|
||||||
status = {"status": "failed", "error_logs": f"{str(e)}", "timeout": timeout}
|
|
||||||
requests.post(callback_url, json={**status, **callback_body})
|
|
||||||
print(f"finished! sending to {callback_url}")
|
|
||||||
pprint({**status, **callback_body})
|
|
||||||
except Exception as e:
|
|
||||||
status = {"status": "failed", "error_logs": str(e)}
|
|
||||||
requests.post(callback_url, json={**status, **callback_body})
|
|
||||||
print(f"finished! sending to {callback_url}")
|
|
||||||
pprint({**status, **callback_body})
|
|
||||||
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
config = {
|
|
||||||
"volume_names": {
|
|
||||||
"user4": {
|
|
||||||
"download_url": "https://civitai.com/api/download/models/11745",
|
|
||||||
"folder_path": "checkpoints"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"volume_paths": {
|
|
||||||
"user4": "/volumes/something",
|
|
||||||
},
|
|
||||||
"callback_url": "",
|
|
||||||
"callback_body": {
|
|
||||||
"model_id": "",
|
|
||||||
"volume_id": "",
|
|
||||||
"folder_path": "checkpoints",
|
|
||||||
},
|
|
||||||
"civitai_api_key": "",
|
|
||||||
}
|
|
||||||
@@ -29,9 +29,7 @@ class ComfyUIDeployExternalText:
|
|||||||
CATEGORY = "text"
|
CATEGORY = "text"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None):
|
def run(self, input_id, default_value=None):
|
||||||
if not input_id or len(input_id.strip()) == 0:
|
return [default_value]
|
||||||
return [default_value]
|
|
||||||
return [input_id]
|
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalText": ComfyUIDeployExternalText}
|
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalText": ComfyUIDeployExternalText}
|
||||||
|
|||||||
@@ -22,10 +22,14 @@ from logging.handlers import RotatingFileHandler
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
import threading
|
import threading
|
||||||
|
import hashlib
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
api = None
|
api = None
|
||||||
api_task = None
|
api_task = None
|
||||||
prompt_metadata = {}
|
prompt_metadata = {}
|
||||||
|
cd_enable_log = os.environ.get('CD_ENABLE_LOG', 'false').lower() == 'true'
|
||||||
|
cd_enable_run_log = os.environ.get('CD_ENABLE_RUN_LOG', 'false').lower() == 'true'
|
||||||
|
|
||||||
def post_prompt(json_data):
|
def post_prompt(json_data):
|
||||||
prompt_server = server.PromptServer.instance
|
prompt_server = server.PromptServer.instance
|
||||||
@@ -97,6 +101,7 @@ async def comfy_deploy_run(request):
|
|||||||
prompt_metadata[prompt_id] = {
|
prompt_metadata[prompt_id] = {
|
||||||
'status_endpoint': data.get('status_endpoint'),
|
'status_endpoint': data.get('status_endpoint'),
|
||||||
'file_upload_endpoint': data.get('file_upload_endpoint'),
|
'file_upload_endpoint': data.get('file_upload_endpoint'),
|
||||||
|
'workflow_api': workflow_api
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -113,6 +118,8 @@ async def comfy_deploy_run(request):
|
|||||||
"stack_trace": stack_trace
|
"stack_trace": stack_trace
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
# When there are critical errors, the prompt is actually not run
|
||||||
|
await update_run(prompt_id, Status.FAILED)
|
||||||
return web.Response(status=500, reason=f"{error_type}: {e}, {stack_trace_short}")
|
return web.Response(status=500, reason=f"{error_type}: {e}, {stack_trace_short}")
|
||||||
|
|
||||||
status = 200
|
status = 200
|
||||||
@@ -141,6 +148,138 @@ async def comfy_deploy_run(request):
|
|||||||
|
|
||||||
sockets = dict()
|
sockets = dict()
|
||||||
|
|
||||||
|
def get_comfyui_path_from_file_path(file_path):
|
||||||
|
file_path_parts = file_path.split("\\")
|
||||||
|
|
||||||
|
if file_path_parts[0] == "input":
|
||||||
|
print("matching input")
|
||||||
|
file_path = os.path.join(folder_paths.get_directory_by_type("input"), *file_path_parts[1:])
|
||||||
|
elif file_path_parts[0] == "models":
|
||||||
|
print("matching models")
|
||||||
|
file_path = folder_paths.get_full_path(file_path_parts[1], os.path.join(*file_path_parts[2:]))
|
||||||
|
|
||||||
|
print(file_path)
|
||||||
|
|
||||||
|
return file_path
|
||||||
|
|
||||||
|
# Form ComfyUI Manager
|
||||||
|
def compute_sha256_checksum(filepath):
|
||||||
|
filepath = get_comfyui_path_from_file_path(filepath)
|
||||||
|
"""Compute the SHA256 checksum of a file, in chunks"""
|
||||||
|
sha256 = hashlib.sha256()
|
||||||
|
with open(filepath, 'rb') as f:
|
||||||
|
for chunk in iter(lambda: f.read(4096), b''):
|
||||||
|
sha256.update(chunk)
|
||||||
|
return sha256.hexdigest()
|
||||||
|
|
||||||
|
# This is start uploading the files to Comfy Deploy
|
||||||
|
@server.PromptServer.instance.routes.post('/comfyui-deploy/upload-file')
|
||||||
|
async def upload_file(request):
|
||||||
|
data = await request.json()
|
||||||
|
|
||||||
|
file_path = data.get("file_path")
|
||||||
|
|
||||||
|
print("Original file path", file_path)
|
||||||
|
|
||||||
|
file_path = get_comfyui_path_from_file_path(file_path)
|
||||||
|
|
||||||
|
# return web.json_response({
|
||||||
|
# "error": f"File not uploaded"
|
||||||
|
# }, status=500)
|
||||||
|
|
||||||
|
token = data.get("token")
|
||||||
|
get_url = data.get("url")
|
||||||
|
|
||||||
|
try:
|
||||||
|
base = folder_paths.base_path
|
||||||
|
file_path = os.path.join(base, file_path)
|
||||||
|
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
file_size = os.path.getsize(file_path)
|
||||||
|
file_extension = os.path.splitext(file_path)[1]
|
||||||
|
|
||||||
|
if file_extension in ['.jpg', '.jpeg']:
|
||||||
|
file_type = 'image/jpeg'
|
||||||
|
elif file_extension == '.png':
|
||||||
|
file_type = 'image/png'
|
||||||
|
elif file_extension == '.webp':
|
||||||
|
file_type = 'image/webp'
|
||||||
|
else:
|
||||||
|
file_type = 'application/octet-stream' # Default to binary file type if unknown
|
||||||
|
else:
|
||||||
|
return web.json_response({
|
||||||
|
"error": f"File not found: {file_path}"
|
||||||
|
}, status=404)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({
|
||||||
|
"error": str(e)
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
|
if get_url:
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
headers = {'Authorization': f'Bearer {token}'}
|
||||||
|
params = {'file_size': file_size, 'type': file_type}
|
||||||
|
async with session.get(get_url, params=params, headers=headers) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
content = await response.json()
|
||||||
|
upload_url = content["upload_url"]
|
||||||
|
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
headers = {
|
||||||
|
"Content-Type": file_type,
|
||||||
|
"x-amz-acl": "public-read",
|
||||||
|
"Content-Length": str(file_size)
|
||||||
|
}
|
||||||
|
async with session.put(upload_url, data=f, headers=headers) as upload_response:
|
||||||
|
if upload_response.status == 200:
|
||||||
|
return web.json_response({
|
||||||
|
"message": "File uploaded successfully",
|
||||||
|
"download_url": content["download_url"]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return web.json_response({
|
||||||
|
"error": f"Failed to upload file to {upload_url}. Status code: {upload_response.status}"
|
||||||
|
}, status=upload_response.status)
|
||||||
|
else:
|
||||||
|
return web.json_response({
|
||||||
|
"error": f"Failed to fetch data from {get_url}. Status code: {response.status}"
|
||||||
|
}, status=response.status)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({
|
||||||
|
"error": f"An error occurred while fetching data from {get_url}: {str(e)}"
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
|
return web.json_response({
|
||||||
|
"error": f"File not uploaded"
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
@server.PromptServer.instance.routes.get('/comfyui-deploy/get-file-hash')
|
||||||
|
async def get_file_hash(request):
|
||||||
|
file_path = request.rel_url.query.get('file_path', '')
|
||||||
|
|
||||||
|
if file_path is None:
|
||||||
|
return web.json_response({
|
||||||
|
"error": "file_path is required"
|
||||||
|
}, status=400)
|
||||||
|
|
||||||
|
try:
|
||||||
|
base = folder_paths.base_path
|
||||||
|
file_path = os.path.join(base, file_path)
|
||||||
|
# print("file_path", file_path)
|
||||||
|
file_hash = compute_sha256_checksum(
|
||||||
|
file_path
|
||||||
|
)
|
||||||
|
return web.json_response({
|
||||||
|
"file_hash": file_hash
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({
|
||||||
|
"error": str(e)
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
@server.PromptServer.instance.routes.get('/comfyui-deploy/ws')
|
@server.PromptServer.instance.routes.get('/comfyui-deploy/ws')
|
||||||
async def websocket_handler(request):
|
async def websocket_handler(request):
|
||||||
ws = web.WebSocketResponse()
|
ws = web.WebSocketResponse()
|
||||||
@@ -157,7 +296,9 @@ async def websocket_handler(request):
|
|||||||
try:
|
try:
|
||||||
# Send initial state to the new client
|
# Send initial state to the new client
|
||||||
await send("status", { 'sid': sid }, sid)
|
await send("status", { 'sid': sid }, sid)
|
||||||
await send_first_time_log(sid)
|
|
||||||
|
if cd_enable_log:
|
||||||
|
await send_first_time_log(sid)
|
||||||
|
|
||||||
async for msg in ws:
|
async for msg in ws:
|
||||||
if msg.type == aiohttp.WSMsgType.ERROR:
|
if msg.type == aiohttp.WSMsgType.ERROR:
|
||||||
@@ -217,6 +358,34 @@ async def send_json_override(self, event, data, sid=None):
|
|||||||
if not have_pending_upload(prompt_id):
|
if not have_pending_upload(prompt_id):
|
||||||
update_run(prompt_id, Status.SUCCESS)
|
update_run(prompt_id, Status.SUCCESS)
|
||||||
|
|
||||||
|
if event == 'executing' and data.get('node') is not None:
|
||||||
|
node = data.get('node')
|
||||||
|
|
||||||
|
if prompt_id in prompt_metadata:
|
||||||
|
if 'progress' not in prompt_metadata[prompt_id]:
|
||||||
|
prompt_metadata[prompt_id]["progress"] = set()
|
||||||
|
|
||||||
|
prompt_metadata[prompt_id]["progress"].add(node)
|
||||||
|
calculated_progress = len(prompt_metadata[prompt_id]["progress"]) / len(prompt_metadata[prompt_id]['workflow_api'])
|
||||||
|
# print("calculated_progress", calculated_progress)
|
||||||
|
|
||||||
|
if 'last_updated_node' in prompt_metadata[prompt_id] and prompt_metadata[prompt_id]['last_updated_node'] == node:
|
||||||
|
return
|
||||||
|
prompt_metadata[prompt_id]['last_updated_node'] = node
|
||||||
|
class_type = prompt_metadata[prompt_id]['workflow_api'][node]['class_type']
|
||||||
|
print("updating run live status", class_type)
|
||||||
|
await update_run_live_status(prompt_id, "Executing " + class_type, calculated_progress)
|
||||||
|
|
||||||
|
if event == 'execution_cached' and data.get('nodes') is not None:
|
||||||
|
if prompt_id in prompt_metadata:
|
||||||
|
if 'progress' not in prompt_metadata[prompt_id]:
|
||||||
|
prompt_metadata[prompt_id]["progress"] = set()
|
||||||
|
|
||||||
|
if 'nodes' in data:
|
||||||
|
for node in data.get('nodes', []):
|
||||||
|
prompt_metadata[prompt_id]["progress"].add(node)
|
||||||
|
# prompt_metadata[prompt_id]["progress"].update(data.get('nodes'))
|
||||||
|
|
||||||
if event == 'execution_error':
|
if event == 'execution_error':
|
||||||
# Careful this might not be fully awaited.
|
# Careful this might not be fully awaited.
|
||||||
await update_run_with_output(prompt_id, data)
|
await update_run_with_output(prompt_id, data)
|
||||||
@@ -236,7 +405,30 @@ class Status(Enum):
|
|||||||
FAILED = "failed"
|
FAILED = "failed"
|
||||||
UPLOADING = "uploading"
|
UPLOADING = "uploading"
|
||||||
|
|
||||||
|
# Global variable to keep track of the last read line number
|
||||||
|
last_read_line_number = 0
|
||||||
|
|
||||||
|
async def update_run_live_status(prompt_id, live_status, calculated_progress: float):
|
||||||
|
if prompt_id not in prompt_metadata:
|
||||||
|
return
|
||||||
|
|
||||||
|
print("progress", calculated_progress)
|
||||||
|
|
||||||
|
status_endpoint = prompt_metadata[prompt_id]['status_endpoint']
|
||||||
|
body = {
|
||||||
|
"run_id": prompt_id,
|
||||||
|
"live_status": live_status,
|
||||||
|
"progress": calculated_progress
|
||||||
|
}
|
||||||
|
# requests.post(status_endpoint, json=body)
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.post(status_endpoint, json=body) as response:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def update_run(prompt_id, status: Status):
|
def update_run(prompt_id, status: Status):
|
||||||
|
global last_read_line_number
|
||||||
|
|
||||||
if prompt_id not in prompt_metadata:
|
if prompt_id not in prompt_metadata:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -251,16 +443,50 @@ def update_run(prompt_id, status: Status):
|
|||||||
"run_id": prompt_id,
|
"run_id": prompt_id,
|
||||||
"status": status.value,
|
"status": status.value,
|
||||||
}
|
}
|
||||||
prompt_metadata[prompt_id]['status'] = status
|
|
||||||
print(f"Status: {status.value}")
|
print(f"Status: {status.value}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
requests.post(status_endpoint, json=body)
|
requests.post(status_endpoint, json=body)
|
||||||
|
|
||||||
|
if cd_enable_run_log and (status == Status.SUCCESS or status == Status.FAILED):
|
||||||
|
try:
|
||||||
|
with open(comfyui_file_path, 'r') as log_file:
|
||||||
|
# log_data = log_file.read()
|
||||||
|
# Move to the last read line
|
||||||
|
all_log_data = log_file.read() # Read all log data
|
||||||
|
print("All log data before skipping:", all_log_data) # Log all data before skipping
|
||||||
|
log_file.seek(0) # Reset file pointer to the beginning
|
||||||
|
|
||||||
|
for _ in range(last_read_line_number):
|
||||||
|
next(log_file)
|
||||||
|
log_data = log_file.read()
|
||||||
|
# Update the last read line number
|
||||||
|
last_read_line_number += log_data.count('\n')
|
||||||
|
print("last_read_line_number", last_read_line_number)
|
||||||
|
print("log_data", log_data)
|
||||||
|
print("log_data.count(n)", log_data.count('\n'))
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"run_id": prompt_id,
|
||||||
|
"log_data": [
|
||||||
|
{
|
||||||
|
"logs": log_data,
|
||||||
|
# "timestamp": time.time(),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
requests.post(status_endpoint, json=body)
|
||||||
|
except Exception as log_error:
|
||||||
|
print(f"Error reading log file: {log_error}")
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_type = type(e).__name__
|
error_type = type(e).__name__
|
||||||
stack_trace = traceback.format_exc().strip()
|
stack_trace = traceback.format_exc().strip()
|
||||||
print(f"Error occurred while updating run: {e} {stack_trace}")
|
print(f"Error occurred while updating run: {e} {stack_trace}")
|
||||||
|
finally:
|
||||||
|
prompt_metadata[prompt_id]['status'] = status
|
||||||
|
|
||||||
|
|
||||||
async def upload_file(prompt_id, filename, subfolder=None, content_type="image/png", type="output"):
|
async def upload_file(prompt_id, filename, subfolder=None, content_type="image/png", type="output"):
|
||||||
"""
|
"""
|
||||||
@@ -310,7 +536,9 @@ async def upload_file(prompt_id, filename, subfolder=None, content_type="image/p
|
|||||||
"Content-Length": str(len(data)),
|
"Content-Length": str(len(data)),
|
||||||
}
|
}
|
||||||
response = requests.put(ok.get("url"), headers=headers, data=data)
|
response = requests.put(ok.get("url"), headers=headers, data=data)
|
||||||
print("upload file response", response.status_code)
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.put(ok.get("url"), headers=headers, data=data) as response:
|
||||||
|
print("upload file response", response.status)
|
||||||
|
|
||||||
def have_pending_upload(prompt_id):
|
def have_pending_upload(prompt_id):
|
||||||
if 'prompt_id' in prompt_metadata and 'uploading_nodes' in prompt_metadata[prompt_id] and len(prompt_metadata[prompt_id]['uploading_nodes']) > 0:
|
if 'prompt_id' in prompt_metadata and 'uploading_nodes' in prompt_metadata[prompt_id] and len(prompt_metadata[prompt_id]['uploading_nodes']) > 0:
|
||||||
@@ -387,20 +615,25 @@ async def update_file_status(prompt_id, data, uploading, have_error=False, node_
|
|||||||
"prompt_id": prompt_id,
|
"prompt_id": prompt_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async def handle_upload(prompt_id, data, key, content_type_key, default_content_type):
|
||||||
|
items = data.get(key, [])
|
||||||
|
for item in items:
|
||||||
|
await upload_file(
|
||||||
|
prompt_id,
|
||||||
|
item.get("filename"),
|
||||||
|
subfolder=item.get("subfolder"),
|
||||||
|
type=item.get("type"),
|
||||||
|
content_type=item.get(content_type_key, default_content_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Upload files in the background
|
# Upload files in the background
|
||||||
async def upload_in_background(prompt_id, data, node_id=None, have_upload=True):
|
async def upload_in_background(prompt_id, data, node_id=None, have_upload=True):
|
||||||
try:
|
try:
|
||||||
images = data.get('images', [])
|
await handle_upload(prompt_id, data, 'images', "content_type", "image/png")
|
||||||
for image in images:
|
await handle_upload(prompt_id, data, 'files', "content_type", "image/png")
|
||||||
await upload_file(prompt_id, image.get("filename"), subfolder=image.get("subfolder"), type=image.get("type"), content_type=image.get("content_type", "image/png"))
|
# This will also be mp4
|
||||||
|
await handle_upload(prompt_id, data, 'gifs', "format", "image/gif")
|
||||||
files = data.get('files', [])
|
|
||||||
for file in files:
|
|
||||||
await upload_file(prompt_id, file.get("filename"), subfolder=file.get("subfolder"), type=file.get("type"), content_type=file.get("content_type", "image/png"))
|
|
||||||
|
|
||||||
gifs = data.get('gifs', [])
|
|
||||||
for gif in gifs:
|
|
||||||
await upload_file(prompt_id, gif.get("filename"), subfolder=gif.get("subfolder"), type=gif.get("type"), content_type=gif.get("format", "image/gif"))
|
|
||||||
|
|
||||||
if have_upload:
|
if have_upload:
|
||||||
await update_file_status(prompt_id, data, False, node_id=node_id)
|
await update_file_status(prompt_id, data, False, node_id=node_id)
|
||||||
@@ -441,6 +674,7 @@ prompt_server.send_json = send_json_override.__get__(prompt_server, server.Promp
|
|||||||
root_path = os.path.dirname(os.path.abspath(__file__))
|
root_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
two_dirs_up = os.path.dirname(os.path.dirname(root_path))
|
two_dirs_up = os.path.dirname(os.path.dirname(root_path))
|
||||||
log_file_path = os.path.join(two_dirs_up, 'comfy-deploy.log')
|
log_file_path = os.path.join(two_dirs_up, 'comfy-deploy.log')
|
||||||
|
comfyui_file_path = os.path.join(two_dirs_up, 'comfyui.log')
|
||||||
|
|
||||||
last_read_line = 0
|
last_read_line = 0
|
||||||
|
|
||||||
@@ -480,4 +714,5 @@ def run_in_new_thread(coroutine):
|
|||||||
t.start()
|
t.start()
|
||||||
asyncio.run_coroutine_threadsafe(coroutine, new_loop)
|
asyncio.run_coroutine_threadsafe(coroutine, new_loop)
|
||||||
|
|
||||||
run_in_new_thread(watch_file_changes(log_file_path, send_logs_to_websocket))
|
if cd_enable_log:
|
||||||
|
run_in_new_thread(watch_file_changes(log_file_path, send_logs_to_websocket))
|
||||||
|
|||||||
@@ -7,45 +7,56 @@ import threading
|
|||||||
import logging
|
import logging
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
handler = RotatingFileHandler('comfy-deploy.log', maxBytes=500000, backupCount=5)
|
# Running with export CD_ENABLE_LOG=true; python main.py
|
||||||
|
|
||||||
original_stdout = sys.stdout
|
# Check for 'cd-enable-log' flag in input arguments
|
||||||
original_stderr = sys.stderr
|
# cd_enable_log = '--cd-enable-log' in sys.argv
|
||||||
|
cd_enable_log = os.environ.get('CD_ENABLE_LOG', 'false').lower() == 'true'
|
||||||
|
|
||||||
class StreamToLogger():
|
def setup():
|
||||||
def __init__(self, log_level):
|
handler = RotatingFileHandler('comfy-deploy.log', maxBytes=500000, backupCount=5)
|
||||||
self.log_level = log_level
|
|
||||||
|
|
||||||
def write(self, buf):
|
original_stdout = sys.stdout
|
||||||
if (self.log_level == logging.INFO):
|
original_stderr = sys.stderr
|
||||||
original_stdout.write(buf)
|
|
||||||
original_stdout.flush()
|
|
||||||
elif (self.log_level == logging.ERROR):
|
|
||||||
original_stderr.write(buf)
|
|
||||||
original_stderr.flush()
|
|
||||||
|
|
||||||
for line in buf.rstrip().splitlines():
|
class StreamToLogger():
|
||||||
handler.handle(
|
def __init__(self, log_level):
|
||||||
logging.LogRecord(
|
self.log_level = log_level
|
||||||
name="comfy-deploy",
|
|
||||||
level=self.log_level,
|
def write(self, buf):
|
||||||
pathname="prestartup_script.py",
|
if (self.log_level == logging.INFO):
|
||||||
lineno=1,
|
original_stdout.write(buf)
|
||||||
msg=line.rstrip(),
|
original_stdout.flush()
|
||||||
args=None,
|
elif (self.log_level == logging.ERROR):
|
||||||
exc_info=None
|
original_stderr.write(buf)
|
||||||
|
original_stderr.flush()
|
||||||
|
|
||||||
|
for line in buf.rstrip().splitlines():
|
||||||
|
handler.handle(
|
||||||
|
logging.LogRecord(
|
||||||
|
name="comfy-deploy",
|
||||||
|
level=self.log_level,
|
||||||
|
pathname="prestartup_script.py",
|
||||||
|
lineno=1,
|
||||||
|
msg=line.rstrip(),
|
||||||
|
args=None,
|
||||||
|
exc_info=None
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
if (self.log_level == logging.INFO):
|
if (self.log_level == logging.INFO):
|
||||||
original_stdout.flush()
|
original_stdout.flush()
|
||||||
elif (self.log_level == logging.ERROR):
|
elif (self.log_level == logging.ERROR):
|
||||||
original_stderr.flush()
|
original_stderr.flush()
|
||||||
|
|
||||||
# Redirect stdout and stderr to the logger
|
# Redirect stdout and stderr to the logger
|
||||||
sys.stdout = StreamToLogger(logging.INFO)
|
sys.stdout = StreamToLogger(logging.INFO)
|
||||||
sys.stderr = StreamToLogger(logging.ERROR)
|
sys.stderr = StreamToLogger(logging.ERROR)
|
||||||
|
|
||||||
|
if cd_enable_log:
|
||||||
|
print("** Comfy Deploy logging enabled")
|
||||||
|
setup()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get the absolute path of the script's directory
|
# Get the absolute path of the script's directory
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { app } from "./app.js";
|
import { app } from "./app.js";
|
||||||
import { api } from "./api.js";
|
import { api } from "./api.js";
|
||||||
import { ComfyWidgets, LGraphNode } from "./widgets.js";
|
import { ComfyWidgets, LGraphNode } from "./widgets.js";
|
||||||
|
import { generateDependencyGraph } from "https://esm.sh/[email protected]";
|
||||||
|
|
||||||
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
|
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
|
||||||
/** @type {ComfyExtension} */
|
/** @type {ComfyExtension} */
|
||||||
@@ -50,11 +51,22 @@ const ext = {
|
|||||||
})
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const { workflow, error } = data;
|
const { workflow, workflow_id, error } = data;
|
||||||
if (error) {
|
if (error) {
|
||||||
infoDialog.showMessage("Unable to load this workflow", error);
|
infoDialog.showMessage("Unable to load this workflow", error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adding a delay to wait for the intial graph to load
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
|
||||||
|
workflow?.nodes.forEach((x) => {
|
||||||
|
if (x?.type === "ComfyDeploy") {
|
||||||
|
x.widgets_values[1] = workflow_id;
|
||||||
|
// x.widgets_values[2] = workflow_version.version;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/** @type {LGraph} */
|
/** @type {LGraph} */
|
||||||
app.loadGraphData(workflow);
|
app.loadGraphData(workflow);
|
||||||
})
|
})
|
||||||
@@ -167,6 +179,92 @@ function showError(title, message) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createDynamicUIHtml(data) {
|
||||||
|
console.log(data);
|
||||||
|
let html =
|
||||||
|
'<div style="min-width: 600px; max-width: 1024px; margin: 14px auto; display: flex; flex-direction: column; gap: 24px;">';
|
||||||
|
const bgcolor = "var(--comfy-input-bg)";
|
||||||
|
const evenBg = "var(--border-color)";
|
||||||
|
const textColor = "var(--input-text)";
|
||||||
|
|
||||||
|
// Custom Nodes
|
||||||
|
html += `<div style="background-color: ${bgcolor}; padding: 24px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);">`;
|
||||||
|
html +=
|
||||||
|
'<h2 style="margin-top: 0px; font-size: 24px; font-weight: bold; margin-bottom: 16px;">Custom Nodes</h2>';
|
||||||
|
|
||||||
|
if (data.missing_nodes?.length > 0) {
|
||||||
|
html += `
|
||||||
|
<div style="border-bottom: 1px solid #e2e8f0; padding: 4px 12px; background-color: ${evenBg}">
|
||||||
|
<h3 style="font-size: 14px; font-weight: semibold; margin-bottom: 8px;">Missing Nodes</h3>
|
||||||
|
<p style="font-size: 12px;">These nodes are not found with any matching custom_nodes in the ComfyUI Manager Database</p>
|
||||||
|
${data.missing_nodes
|
||||||
|
.map((node) => {
|
||||||
|
return `<p style="font-size: 14px; color: #d69e2e;">${node}</p>`;
|
||||||
|
})
|
||||||
|
.join("")}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.values(data.custom_nodes).forEach((node) => {
|
||||||
|
html += `
|
||||||
|
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 16px;">
|
||||||
|
<a href="${
|
||||||
|
node.url
|
||||||
|
}" target="_blank" style="font-size: 18px; font-weight: semibold; color: white; text-decoration: none;">${
|
||||||
|
node.name
|
||||||
|
}</a>
|
||||||
|
<p style="font-size: 14px; color: #4b5563;">${node.hash}</p>
|
||||||
|
${
|
||||||
|
node.warning
|
||||||
|
? `<p style="font-size: 14px; color: #d69e2e;">${node.warning}</p>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
html += "</div>";
|
||||||
|
|
||||||
|
// Models
|
||||||
|
html += `<div style="background-color: ${bgcolor}; padding: 24px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);">`;
|
||||||
|
html +=
|
||||||
|
'<h2 style="margin-top: 0px; font-size: 24px; font-weight: bold; margin-bottom: 16px;">Models</h2>';
|
||||||
|
|
||||||
|
Object.entries(data.models).forEach(([section, items]) => {
|
||||||
|
html += `
|
||||||
|
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 8px; padding-bottom: 8px;">
|
||||||
|
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">${
|
||||||
|
section.charAt(0).toUpperCase() + section.slice(1)
|
||||||
|
}</h3>`;
|
||||||
|
items.forEach((item) => {
|
||||||
|
html += `<p style="font-size: 14px; color: ${textColor};">${item.name}</p>`;
|
||||||
|
});
|
||||||
|
html += "</div>";
|
||||||
|
});
|
||||||
|
html += "</div>";
|
||||||
|
|
||||||
|
// Models
|
||||||
|
html += `<div style="background-color: ${bgcolor}; padding: 24px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);">`;
|
||||||
|
html +=
|
||||||
|
'<h2 style="margin-top: 0px; font-size: 24px; font-weight: bold; margin-bottom: 16px;">Files</h2>';
|
||||||
|
|
||||||
|
Object.entries(data.files).forEach(([section, items]) => {
|
||||||
|
html += `
|
||||||
|
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 8px; padding-bottom: 8px;">
|
||||||
|
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">${
|
||||||
|
section.charAt(0).toUpperCase() + section.slice(1)
|
||||||
|
}</h3>`;
|
||||||
|
items.forEach((item) => {
|
||||||
|
html += `<p style="font-size: 14px; color: ${textColor};">${item.name}</p>`;
|
||||||
|
});
|
||||||
|
html += "</div>";
|
||||||
|
});
|
||||||
|
html += "</div>";
|
||||||
|
|
||||||
|
html += "</div>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
function addButton() {
|
function addButton() {
|
||||||
const menu = document.querySelector(".comfy-menu");
|
const menu = document.querySelector(".comfy-menu");
|
||||||
|
|
||||||
@@ -178,8 +276,33 @@ function addButton() {
|
|||||||
/** @type {LGraph} */
|
/** @type {LGraph} */
|
||||||
const graph = app.graph;
|
const graph = app.graph;
|
||||||
|
|
||||||
|
let { endpoint, apiKey, displayName } = getData();
|
||||||
|
|
||||||
|
if (!endpoint || !apiKey || apiKey === "" || endpoint === "") {
|
||||||
|
configDialog.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ok = await confirmDialog.confirm(
|
||||||
|
"Confirm deployment -> " +
|
||||||
|
displayName +
|
||||||
|
"<br><br><small><div style='font-weight: normal;'>" +
|
||||||
|
endpoint +
|
||||||
|
"<div><br>",
|
||||||
|
`A new version will be deployed, are you confirm? <br><br><input id="include-deps" type="checkbox" checked>Include dependence</input>`,
|
||||||
|
);
|
||||||
|
if (!ok) return;
|
||||||
|
|
||||||
|
const includeDeps = document.getElementById("include-deps").checked;
|
||||||
|
|
||||||
|
if (endpoint.endsWith("/")) {
|
||||||
|
endpoint = endpoint.slice(0, -1);
|
||||||
|
}
|
||||||
|
loadingDialog.showLoading("Generating snapshot", "Please wait...");
|
||||||
|
|
||||||
const snapshot = await fetch("/snapshot/get_current").then((x) => x.json());
|
const snapshot = await fetch("/snapshot/get_current").then((x) => x.json());
|
||||||
// console.log(snapshot);
|
// console.log(snapshot);
|
||||||
|
loadingDialog.close();
|
||||||
|
|
||||||
if (!snapshot) {
|
if (!snapshot) {
|
||||||
showError(
|
showError(
|
||||||
@@ -213,32 +336,94 @@ function addButton() {
|
|||||||
|
|
||||||
const deployMetaNode = deployMeta[0];
|
const deployMetaNode = deployMeta[0];
|
||||||
|
|
||||||
// console.log(deployMetaNode);
|
|
||||||
|
|
||||||
const workflow_name = deployMetaNode.widgets[0].value;
|
const workflow_name = deployMetaNode.widgets[0].value;
|
||||||
const workflow_id = deployMetaNode.widgets[1].value;
|
const workflow_id = deployMetaNode.widgets[1].value;
|
||||||
|
|
||||||
console.log(workflow_name, workflow_id);
|
|
||||||
|
|
||||||
const prompt = await app.graphToPrompt();
|
const prompt = await app.graphToPrompt();
|
||||||
console.log(graph);
|
let deps = undefined;
|
||||||
console.log(prompt);
|
|
||||||
|
|
||||||
// const endpoint = localStorage.getItem("endpoint") ?? "";
|
if (includeDeps) {
|
||||||
// const apiKey = localStorage.getItem("apiKey");
|
loadingDialog.showLoading("Fetching existing version", "Please wait...");
|
||||||
|
|
||||||
const { endpoint, apiKey, displayName } = getData();
|
const existing_workflow = await fetch(
|
||||||
|
endpoint + "/api/workflow/" + workflow_id,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: "Bearer " + apiKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((x) => x.json())
|
||||||
|
.catch(() => {
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
if (!endpoint || !apiKey || apiKey === "" || endpoint === "") {
|
loadingDialog.close();
|
||||||
configDialog.show();
|
|
||||||
return;
|
loadingDialog.showLoading(
|
||||||
|
"Generating dependency graph",
|
||||||
|
"Please wait...",
|
||||||
|
);
|
||||||
|
deps = await generateDependencyGraph({
|
||||||
|
workflow_api: prompt.output,
|
||||||
|
snapshot: snapshot,
|
||||||
|
computeFileHash: async (file) => {
|
||||||
|
console.log(file);
|
||||||
|
loadingDialog.showLoading("Generating hash", file);
|
||||||
|
const hash = await fetch(
|
||||||
|
`/comfyui-deploy/get-file-hash?file_path=${encodeURIComponent(
|
||||||
|
file,
|
||||||
|
)}`,
|
||||||
|
).then((x) => x.json());
|
||||||
|
loadingDialog.showLoading("Generating hash", file);
|
||||||
|
console.log(hash);
|
||||||
|
return hash.file_hash;
|
||||||
|
},
|
||||||
|
handleFileUpload: async (file, hash, prevhash) => {
|
||||||
|
console.log("Uploading ", file);
|
||||||
|
loadingDialog.showLoading("Uploading file", file);
|
||||||
|
try {
|
||||||
|
const { download_url } = await fetch(
|
||||||
|
`/comfyui-deploy/upload-file`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
file_path: file,
|
||||||
|
token: apiKey,
|
||||||
|
url: endpoint + "/api/upload-url",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((x) => x.json())
|
||||||
|
.catch(() => {
|
||||||
|
loadingDialog.close();
|
||||||
|
confirmDialog.confirm("Error", "Unable to upload file " + file);
|
||||||
|
});
|
||||||
|
loadingDialog.showLoading("Uploaded file", file);
|
||||||
|
console.log(download_url);
|
||||||
|
return download_url;
|
||||||
|
} catch (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
existingDependencies: existing_workflow.dependencies,
|
||||||
|
});
|
||||||
|
|
||||||
|
loadingDialog.close();
|
||||||
|
|
||||||
|
const depsOk = await confirmDialog.confirm(
|
||||||
|
"Check dependencies",
|
||||||
|
// JSON.stringify(deps, null, 2),
|
||||||
|
createDynamicUIHtml(deps),
|
||||||
|
);
|
||||||
|
if (!depsOk) return;
|
||||||
|
|
||||||
|
console.log(deps);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ok = await confirmDialog.confirm(
|
loadingDialog.showLoading("Deploying...");
|
||||||
"Confirm deployment -> " + displayName,
|
|
||||||
"A new version will be deployed, are you conform?",
|
|
||||||
);
|
|
||||||
if (!ok) return;
|
|
||||||
|
|
||||||
title.innerText = "Deploying...";
|
title.innerText = "Deploying...";
|
||||||
title.style.color = "orange";
|
title.style.color = "orange";
|
||||||
@@ -250,6 +435,8 @@ function addButton() {
|
|||||||
endpoint = endpoint.slice(0, -1);
|
endpoint = endpoint.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// console.log(prompt.workflow);
|
||||||
|
|
||||||
const apiRoute = endpoint + "/api/workflow";
|
const apiRoute = endpoint + "/api/workflow";
|
||||||
// const userId = apiKey
|
// const userId = apiKey
|
||||||
try {
|
try {
|
||||||
@@ -259,6 +446,7 @@ function addButton() {
|
|||||||
workflow: prompt.workflow,
|
workflow: prompt.workflow,
|
||||||
workflow_api: prompt.output,
|
workflow_api: prompt.output,
|
||||||
snapshot: snapshot,
|
snapshot: snapshot,
|
||||||
|
dependencies: deps,
|
||||||
};
|
};
|
||||||
console.log(body);
|
console.log(body);
|
||||||
let data = await fetch(apiRoute, {
|
let data = await fetch(apiRoute, {
|
||||||
@@ -278,6 +466,8 @@ function addButton() {
|
|||||||
data = await data.json();
|
data = await data.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadingDialog.close();
|
||||||
|
|
||||||
title.textContent = "Done";
|
title.textContent = "Done";
|
||||||
title.style.color = "green";
|
title.style.color = "green";
|
||||||
|
|
||||||
@@ -294,6 +484,7 @@ function addButton() {
|
|||||||
title.style.color = "white";
|
title.style.color = "white";
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
loadingDialog.close();
|
||||||
app.ui.dialog.show(e);
|
app.ui.dialog.show(e);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
title.textContent = "Error";
|
title.textContent = "Error";
|
||||||
@@ -370,7 +561,7 @@ export class InfoDialog extends ComfyDialog {
|
|||||||
|
|
||||||
showMessage(title, message) {
|
showMessage(title, message) {
|
||||||
this.show(`
|
this.show(`
|
||||||
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
<div style="width: 100%; max-width: 600px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
||||||
<h3 style="margin: 0px;">${title}</h3>
|
<h3 style="margin: 0px;">${title}</h3>
|
||||||
<label>
|
<label>
|
||||||
${message}
|
${message}
|
||||||
@@ -426,7 +617,10 @@ export class LoadingDialog extends ComfyDialog {
|
|||||||
showLoading(title, message) {
|
showLoading(title, message) {
|
||||||
this.show(`
|
this.show(`
|
||||||
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
||||||
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 4px;">${title} ${this.loadingIcon}</h3>
|
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 12px;">${title} ${
|
||||||
|
this.loadingIcon
|
||||||
|
}</h3>
|
||||||
|
${message ? `<label>${message}</label>` : ""}
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
@@ -545,7 +739,7 @@ export class ConfirmDialog extends InfoDialog {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.callback = resolve;
|
this.callback = resolve;
|
||||||
this.show(`
|
this.show(`
|
||||||
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
<div style="width: 100%; max-width: 600px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
||||||
<h3 style="margin: 0px;">${title}</h3>
|
<h3 style="margin: 0px;">${title}</h3>
|
||||||
<label>
|
<label>
|
||||||
${message}
|
${message}
|
||||||
@@ -682,16 +876,19 @@ export class ConfigDialog extends ComfyDialog {
|
|||||||
</label>
|
</label>
|
||||||
<label style="color: white; width: 100%;">
|
<label style="color: white; width: 100%;">
|
||||||
Endpoint:
|
Endpoint:
|
||||||
<input id="endpoint" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="text" value="${data.endpoint
|
<input id="endpoint" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="text" value="${
|
||||||
}">
|
data.endpoint
|
||||||
|
}">
|
||||||
</label>
|
</label>
|
||||||
<label style="color: white;">
|
<label style="color: white;">
|
||||||
API Key: ${data.displayName ?? ""}
|
API Key: ${data.displayName ?? ""}
|
||||||
<input id="apiKey" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="password" value="${data.apiKey
|
<input id="apiKey" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="password" value="${
|
||||||
}">
|
data.apiKey
|
||||||
|
}">
|
||||||
<button id="loginButton" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;">
|
<button id="loginButton" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;">
|
||||||
${data.apiKey ? "Re-login with ComfyDeploy" : "Login with ComfyDeploy"
|
${
|
||||||
}
|
data.apiKey ? "Re-login with ComfyDeploy" : "Login with ComfyDeploy"
|
||||||
|
}
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,14 +20,3 @@ PLAUSIBLE_DOMAIN=
|
|||||||
|
|
||||||
NEXT_PUBLIC_POSTHOG_KEY="your-api-key"
|
NEXT_PUBLIC_POSTHOG_KEY="your-api-key"
|
||||||
NEXT_PUBLIC_POSTHOG_HOST="your-ph-address"
|
NEXT_PUBLIC_POSTHOG_HOST="your-ph-address"
|
||||||
|
|
||||||
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/auth/sign-in
|
|
||||||
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/auth/sign-up
|
|
||||||
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
|
|
||||||
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
|
|
||||||
|
|
||||||
STRIPE_API_KEY="sk_test_"
|
|
||||||
STRIPE_PR_PRO="price_"
|
|
||||||
STRIPE_PR_ENTERPRISE="price_"
|
|
||||||
STRIPE_PR_API="price_"
|
|
||||||
STRIPE_WEBHOOK_SECRET="whsec_"
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS "comfyui_deploy"."user_usage" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
||||||
"org_id" text,
|
|
||||||
"user_id" text NOT NULL,
|
|
||||||
"usage_time" real DEFAULT 0 NOT NULL,
|
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
"ended_at" timestamp DEFAULT now() NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."user_usage" ADD CONSTRAINT "user_usage_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
DO $$ BEGIN
|
|
||||||
CREATE TYPE "subscription_plan" AS ENUM('basic', 'pro', 'enterprise');
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
CREATE TYPE "subscription_plan_status" AS ENUM('active', 'deleted', 'paused');
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "comfyui_deploy"."subscription_status" (
|
|
||||||
"stripe_customer_id" text PRIMARY KEY NOT NULL,
|
|
||||||
"user_id" text,
|
|
||||||
"org_id" text,
|
|
||||||
"plan" "subscription_plan" NOT NULL,
|
|
||||||
"status" "subscription_plan_status" NOT NULL,
|
|
||||||
"subscription_plan_id" text,
|
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
||||||
);
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."subscription_status" RENAME COLUMN "subscription_plan_id" TO "subscription_id";--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."subscription_status" ADD COLUMN "subscription_item_plan_id" text;--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."subscription_status" ADD COLUMN "subscription_item_api_id" text;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."subscription_status" ADD COLUMN "cancel_at_period_end" boolean DEFAULT false;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "user_id" text;--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "org_id" text;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "gpu" "machine_gpu";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "machine_type" "machine_type";
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
DO $$ BEGIN
|
|
||||||
CREATE TYPE "model_upload_type" AS ENUM('civitai', 'huggingface', 'other');
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
CREATE TYPE "resource_upload" AS ENUM('started', 'success', 'failed');
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoints" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
||||||
"user_id" text,
|
|
||||||
"org_id" text,
|
|
||||||
"description" text,
|
|
||||||
"checkpoint_volume_id" uuid NOT NULL,
|
|
||||||
"model_name" text,
|
|
||||||
"folder_path" text,
|
|
||||||
"civitai_id" text,
|
|
||||||
"civitai_version_id" text,
|
|
||||||
"civitai_url" text,
|
|
||||||
"civitai_download_url" text,
|
|
||||||
"civitai_model_response" jsonb,
|
|
||||||
"hf_url" text,
|
|
||||||
"s3_url" text,
|
|
||||||
"client_url" text,
|
|
||||||
"is_public" boolean DEFAULT false NOT NULL,
|
|
||||||
"status" "resource_upload" DEFAULT 'started' NOT NULL,
|
|
||||||
"upload_machine_id" text,
|
|
||||||
"upload_type" "model_upload_type" NOT NULL,
|
|
||||||
"error_log" text,
|
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoint_volume" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
||||||
"user_id" text,
|
|
||||||
"org_id" text,
|
|
||||||
"volume_name" text NOT NULL,
|
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
"updated_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
"disabled" boolean DEFAULT false NOT NULL
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_checkpoint_volume_id_checkpoint_volume_id_fk" FOREIGN KEY ("checkpoint_volume_id") REFERENCES "comfyui_deploy"."checkpoint_volume"("id") ON DELETE cascade ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."checkpoint_volume" ADD CONSTRAINT "checkpoint_volume_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
DO $$ BEGIN
|
|
||||||
CREATE TYPE "model_type" AS ENUM('checkpoint', 'lora', 'embedding', 'vae');
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."checkpoints" RENAME TO "models";--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."checkpoint_volume" RENAME TO "user_volume";--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."models" RENAME COLUMN "checkpoint_volume_id" TO "user_volume_id";--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."models" DROP CONSTRAINT "checkpoints_user_id_users_id_fk";
|
|
||||||
--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."models" DROP CONSTRAINT "checkpoints_checkpoint_volume_id_checkpoint_volume_id_fk";
|
|
||||||
--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."user_volume" DROP CONSTRAINT "checkpoint_volume_user_id_users_id_fk";
|
|
||||||
--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."models" ADD COLUMN "model_type" "model_type" DEFAULT 'checkpoint' NOT NULL;--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."models" ADD CONSTRAINT "models_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."models" ADD CONSTRAINT "models_user_volume_id_user_volume_id_fk" FOREIGN KEY ("user_volume_id") REFERENCES "comfyui_deploy"."user_volume"("id") ON DELETE cascade ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."user_volume" ADD CONSTRAINT "user_volume_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."models" ALTER COLUMN "is_public" SET DEFAULT true;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ALTER TYPE "workflow_run_status" ADD VALUE 'started';--> statement-breakpoint
|
|
||||||
ALTER TYPE "workflow_run_status" ADD VALUE 'queued';--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "queued_at" timestamp;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TYPE "model_upload_type" ADD VALUE 'download_url';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TYPE "model_upload_type" ADD VALUE 'download-url';
|
|
||||||
@@ -1,894 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "2b2a5d0a-2494-45ea-9d4a-58a70df97829",
|
|
||||||
"prevId": "8d654f92-7f7e-420f-bbd3-73b6b27adf35",
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"auth_requests": {
|
|
||||||
"name": "auth_requests",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"request_id": {
|
|
||||||
"name": "request_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"api_hash": {
|
|
||||||
"name": "api_hash",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"expired_date": {
|
|
||||||
"name": "expired_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"deployments": {
|
|
||||||
"name": "deployments",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"share_slug": {
|
|
||||||
"name": "share_slug",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"showcase_media": {
|
|
||||||
"name": "showcase_media",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"environment": {
|
|
||||||
"name": "environment",
|
|
||||||
"type": "deployment_environment",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"deployments_user_id_users_id_fk": {
|
|
||||||
"name": "deployments_user_id_users_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "deployments_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "deployments_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_machine_id_machines_id_fk": {
|
|
||||||
"name": "deployments_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"deployments_share_slug_unique": {
|
|
||||||
"name": "deployments_share_slug_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"share_slug"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machines": {
|
|
||||||
"name": "machines",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"endpoint": {
|
|
||||||
"name": "endpoint",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"disabled": {
|
|
||||||
"name": "disabled",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"auth_token": {
|
|
||||||
"name": "auth_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"name": "type",
|
|
||||||
"type": "machine_type",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'classic'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "machine_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'ready'"
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"models": {
|
|
||||||
"name": "models",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"gpu": {
|
|
||||||
"name": "gpu",
|
|
||||||
"type": "machine_gpu",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_machine_instance_id": {
|
|
||||||
"name": "build_machine_instance_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_log": {
|
|
||||||
"name": "build_log",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"machines_user_id_users_id_fk": {
|
|
||||||
"name": "machines_user_id_users_id_fk",
|
|
||||||
"tableFrom": "machines",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"user_usage": {
|
|
||||||
"name": "user_usage",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"usage_time": {
|
|
||||||
"name": "usage_time",
|
|
||||||
"type": "real",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"user_usage_user_id_users_id_fk": {
|
|
||||||
"name": "user_usage_user_id_users_id_fk",
|
|
||||||
"tableFrom": "user_usage",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_run_outputs": {
|
|
||||||
"name": "workflow_run_outputs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"run_id": {
|
|
||||||
"name": "run_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"name": "data",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
|
|
||||||
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
|
|
||||||
"tableFrom": "workflow_run_outputs",
|
|
||||||
"tableTo": "workflow_runs",
|
|
||||||
"columnsFrom": [
|
|
||||||
"run_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_runs": {
|
|
||||||
"name": "workflow_runs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_inputs": {
|
|
||||||
"name": "workflow_inputs",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"origin": {
|
|
||||||
"name": "origin",
|
|
||||||
"type": "workflow_run_origin",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'api'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "workflow_run_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'not-started'"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"started_at": {
|
|
||||||
"name": "started_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_machine_id_machines_id_fk": {
|
|
||||||
"name": "workflow_runs_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflows": {
|
|
||||||
"name": "workflows",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflows_user_id_users_id_fk": {
|
|
||||||
"name": "workflows_user_id_users_id_fk",
|
|
||||||
"tableFrom": "workflows",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_versions": {
|
|
||||||
"name": "workflow_versions",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow": {
|
|
||||||
"name": "workflow",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_api": {
|
|
||||||
"name": "workflow_api",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"name": "version",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_versions_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_versions_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_versions",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"deployment_environment": {
|
|
||||||
"name": "deployment_environment",
|
|
||||||
"values": {
|
|
||||||
"staging": "staging",
|
|
||||||
"production": "production",
|
|
||||||
"public-share": "public-share"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_gpu": {
|
|
||||||
"name": "machine_gpu",
|
|
||||||
"values": {
|
|
||||||
"T4": "T4",
|
|
||||||
"A10G": "A10G",
|
|
||||||
"A100": "A100"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_status": {
|
|
||||||
"name": "machine_status",
|
|
||||||
"values": {
|
|
||||||
"ready": "ready",
|
|
||||||
"building": "building",
|
|
||||||
"error": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_type": {
|
|
||||||
"name": "machine_type",
|
|
||||||
"values": {
|
|
||||||
"classic": "classic",
|
|
||||||
"runpod-serverless": "runpod-serverless",
|
|
||||||
"modal-serverless": "modal-serverless",
|
|
||||||
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"workflow_run_origin": {
|
|
||||||
"name": "workflow_run_origin",
|
|
||||||
"values": {
|
|
||||||
"manual": "manual",
|
|
||||||
"api": "api",
|
|
||||||
"public-share": "public-share"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"workflow_run_status": {
|
|
||||||
"name": "workflow_run_status",
|
|
||||||
"values": {
|
|
||||||
"not-started": "not-started",
|
|
||||||
"running": "running",
|
|
||||||
"uploading": "uploading",
|
|
||||||
"success": "success",
|
|
||||||
"failed": "failed"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"schemas": {
|
|
||||||
"comfyui_deploy": "comfyui_deploy"
|
|
||||||
},
|
|
||||||
"_meta": {
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {},
|
|
||||||
"columns": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,970 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "4db08fc3-2444-42da-b742-f1832ad8caf1",
|
|
||||||
"prevId": "2b2a5d0a-2494-45ea-9d4a-58a70df97829",
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"auth_requests": {
|
|
||||||
"name": "auth_requests",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"request_id": {
|
|
||||||
"name": "request_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"api_hash": {
|
|
||||||
"name": "api_hash",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"expired_date": {
|
|
||||||
"name": "expired_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"deployments": {
|
|
||||||
"name": "deployments",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"share_slug": {
|
|
||||||
"name": "share_slug",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"showcase_media": {
|
|
||||||
"name": "showcase_media",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"environment": {
|
|
||||||
"name": "environment",
|
|
||||||
"type": "deployment_environment",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"deployments_user_id_users_id_fk": {
|
|
||||||
"name": "deployments_user_id_users_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "deployments_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "deployments_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_machine_id_machines_id_fk": {
|
|
||||||
"name": "deployments_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"deployments_share_slug_unique": {
|
|
||||||
"name": "deployments_share_slug_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"share_slug"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machines": {
|
|
||||||
"name": "machines",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"endpoint": {
|
|
||||||
"name": "endpoint",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"disabled": {
|
|
||||||
"name": "disabled",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"auth_token": {
|
|
||||||
"name": "auth_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"name": "type",
|
|
||||||
"type": "machine_type",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'classic'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "machine_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'ready'"
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"models": {
|
|
||||||
"name": "models",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"gpu": {
|
|
||||||
"name": "gpu",
|
|
||||||
"type": "machine_gpu",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_machine_instance_id": {
|
|
||||||
"name": "build_machine_instance_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_log": {
|
|
||||||
"name": "build_log",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"machines_user_id_users_id_fk": {
|
|
||||||
"name": "machines_user_id_users_id_fk",
|
|
||||||
"tableFrom": "machines",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"subscription_status": {
|
|
||||||
"name": "subscription_status",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"stripe_customer_id": {
|
|
||||||
"name": "stripe_customer_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"plan": {
|
|
||||||
"name": "plan",
|
|
||||||
"type": "subscription_plan",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "subscription_plan_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"subscription_plan_id": {
|
|
||||||
"name": "subscription_plan_id",
|
|
||||||
"type": "text",
|
|
||||||
"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": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"user_usage": {
|
|
||||||
"name": "user_usage",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"usage_time": {
|
|
||||||
"name": "usage_time",
|
|
||||||
"type": "real",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"user_usage_user_id_users_id_fk": {
|
|
||||||
"name": "user_usage_user_id_users_id_fk",
|
|
||||||
"tableFrom": "user_usage",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_run_outputs": {
|
|
||||||
"name": "workflow_run_outputs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"run_id": {
|
|
||||||
"name": "run_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"name": "data",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
|
|
||||||
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
|
|
||||||
"tableFrom": "workflow_run_outputs",
|
|
||||||
"tableTo": "workflow_runs",
|
|
||||||
"columnsFrom": [
|
|
||||||
"run_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_runs": {
|
|
||||||
"name": "workflow_runs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_inputs": {
|
|
||||||
"name": "workflow_inputs",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"origin": {
|
|
||||||
"name": "origin",
|
|
||||||
"type": "workflow_run_origin",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'api'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "workflow_run_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'not-started'"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"started_at": {
|
|
||||||
"name": "started_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_machine_id_machines_id_fk": {
|
|
||||||
"name": "workflow_runs_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflows": {
|
|
||||||
"name": "workflows",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflows_user_id_users_id_fk": {
|
|
||||||
"name": "workflows_user_id_users_id_fk",
|
|
||||||
"tableFrom": "workflows",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_versions": {
|
|
||||||
"name": "workflow_versions",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow": {
|
|
||||||
"name": "workflow",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_api": {
|
|
||||||
"name": "workflow_api",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"name": "version",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_versions_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_versions_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_versions",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"deployment_environment": {
|
|
||||||
"name": "deployment_environment",
|
|
||||||
"values": {
|
|
||||||
"staging": "staging",
|
|
||||||
"production": "production",
|
|
||||||
"public-share": "public-share"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_gpu": {
|
|
||||||
"name": "machine_gpu",
|
|
||||||
"values": {
|
|
||||||
"T4": "T4",
|
|
||||||
"A10G": "A10G",
|
|
||||||
"A100": "A100"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_status": {
|
|
||||||
"name": "machine_status",
|
|
||||||
"values": {
|
|
||||||
"ready": "ready",
|
|
||||||
"building": "building",
|
|
||||||
"error": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_type": {
|
|
||||||
"name": "machine_type",
|
|
||||||
"values": {
|
|
||||||
"classic": "classic",
|
|
||||||
"runpod-serverless": "runpod-serverless",
|
|
||||||
"modal-serverless": "modal-serverless",
|
|
||||||
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subscription_plan": {
|
|
||||||
"name": "subscription_plan",
|
|
||||||
"values": {
|
|
||||||
"basic": "basic",
|
|
||||||
"pro": "pro",
|
|
||||||
"enterprise": "enterprise"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subscription_plan_status": {
|
|
||||||
"name": "subscription_plan_status",
|
|
||||||
"values": {
|
|
||||||
"active": "active",
|
|
||||||
"deleted": "deleted",
|
|
||||||
"paused": "paused"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,984 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "cde8f758-0055-4326-981d-293ac84db54a",
|
|
||||||
"prevId": "4db08fc3-2444-42da-b742-f1832ad8caf1",
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"auth_requests": {
|
|
||||||
"name": "auth_requests",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"request_id": {
|
|
||||||
"name": "request_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"api_hash": {
|
|
||||||
"name": "api_hash",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"expired_date": {
|
|
||||||
"name": "expired_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"deployments": {
|
|
||||||
"name": "deployments",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"share_slug": {
|
|
||||||
"name": "share_slug",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"showcase_media": {
|
|
||||||
"name": "showcase_media",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"environment": {
|
|
||||||
"name": "environment",
|
|
||||||
"type": "deployment_environment",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"deployments_user_id_users_id_fk": {
|
|
||||||
"name": "deployments_user_id_users_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "deployments_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "deployments_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_machine_id_machines_id_fk": {
|
|
||||||
"name": "deployments_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"deployments_share_slug_unique": {
|
|
||||||
"name": "deployments_share_slug_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"share_slug"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machines": {
|
|
||||||
"name": "machines",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"endpoint": {
|
|
||||||
"name": "endpoint",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"disabled": {
|
|
||||||
"name": "disabled",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"auth_token": {
|
|
||||||
"name": "auth_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"name": "type",
|
|
||||||
"type": "machine_type",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'classic'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "machine_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'ready'"
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"models": {
|
|
||||||
"name": "models",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"gpu": {
|
|
||||||
"name": "gpu",
|
|
||||||
"type": "machine_gpu",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_machine_instance_id": {
|
|
||||||
"name": "build_machine_instance_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_log": {
|
|
||||||
"name": "build_log",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"machines_user_id_users_id_fk": {
|
|
||||||
"name": "machines_user_id_users_id_fk",
|
|
||||||
"tableFrom": "machines",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"subscription_status": {
|
|
||||||
"name": "subscription_status",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"stripe_customer_id": {
|
|
||||||
"name": "stripe_customer_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"plan": {
|
|
||||||
"name": "plan",
|
|
||||||
"type": "subscription_plan",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "subscription_plan_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"subscription_id": {
|
|
||||||
"name": "subscription_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"subscription_item_plan_id": {
|
|
||||||
"name": "subscription_item_plan_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"subscription_item_api_id": {
|
|
||||||
"name": "subscription_item_api_id",
|
|
||||||
"type": "text",
|
|
||||||
"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": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"user_usage": {
|
|
||||||
"name": "user_usage",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"usage_time": {
|
|
||||||
"name": "usage_time",
|
|
||||||
"type": "real",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"user_usage_user_id_users_id_fk": {
|
|
||||||
"name": "user_usage_user_id_users_id_fk",
|
|
||||||
"tableFrom": "user_usage",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_run_outputs": {
|
|
||||||
"name": "workflow_run_outputs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"run_id": {
|
|
||||||
"name": "run_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"name": "data",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
|
|
||||||
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
|
|
||||||
"tableFrom": "workflow_run_outputs",
|
|
||||||
"tableTo": "workflow_runs",
|
|
||||||
"columnsFrom": [
|
|
||||||
"run_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_runs": {
|
|
||||||
"name": "workflow_runs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_inputs": {
|
|
||||||
"name": "workflow_inputs",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"origin": {
|
|
||||||
"name": "origin",
|
|
||||||
"type": "workflow_run_origin",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'api'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "workflow_run_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'not-started'"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"started_at": {
|
|
||||||
"name": "started_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_machine_id_machines_id_fk": {
|
|
||||||
"name": "workflow_runs_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflows": {
|
|
||||||
"name": "workflows",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflows_user_id_users_id_fk": {
|
|
||||||
"name": "workflows_user_id_users_id_fk",
|
|
||||||
"tableFrom": "workflows",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_versions": {
|
|
||||||
"name": "workflow_versions",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow": {
|
|
||||||
"name": "workflow",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_api": {
|
|
||||||
"name": "workflow_api",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"name": "version",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_versions_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_versions_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_versions",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"deployment_environment": {
|
|
||||||
"name": "deployment_environment",
|
|
||||||
"values": {
|
|
||||||
"staging": "staging",
|
|
||||||
"production": "production",
|
|
||||||
"public-share": "public-share"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_gpu": {
|
|
||||||
"name": "machine_gpu",
|
|
||||||
"values": {
|
|
||||||
"T4": "T4",
|
|
||||||
"A10G": "A10G",
|
|
||||||
"A100": "A100"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_status": {
|
|
||||||
"name": "machine_status",
|
|
||||||
"values": {
|
|
||||||
"ready": "ready",
|
|
||||||
"building": "building",
|
|
||||||
"error": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_type": {
|
|
||||||
"name": "machine_type",
|
|
||||||
"values": {
|
|
||||||
"classic": "classic",
|
|
||||||
"runpod-serverless": "runpod-serverless",
|
|
||||||
"modal-serverless": "modal-serverless",
|
|
||||||
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subscription_plan": {
|
|
||||||
"name": "subscription_plan",
|
|
||||||
"values": {
|
|
||||||
"basic": "basic",
|
|
||||||
"pro": "pro",
|
|
||||||
"enterprise": "enterprise"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subscription_plan_status": {
|
|
||||||
"name": "subscription_plan_status",
|
|
||||||
"values": {
|
|
||||||
"active": "active",
|
|
||||||
"deleted": "deleted",
|
|
||||||
"paused": "paused"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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": {
|
|
||||||
"\"comfyui_deploy\".\"subscription_status\".\"subscription_plan_id\"": "\"comfyui_deploy\".\"subscription_status\".\"subscription_id\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,989 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bd893271-b9ea-4832-a3e8-a6970b9f20d9",
|
|
||||||
"prevId": "cde8f758-0055-4326-981d-293ac84db54a",
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"auth_requests": {
|
|
||||||
"name": "auth_requests",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"request_id": {
|
|
||||||
"name": "request_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"api_hash": {
|
|
||||||
"name": "api_hash",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"expired_date": {
|
|
||||||
"name": "expired_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"deployments": {
|
|
||||||
"name": "deployments",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"share_slug": {
|
|
||||||
"name": "share_slug",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"showcase_media": {
|
|
||||||
"name": "showcase_media",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"environment": {
|
|
||||||
"name": "environment",
|
|
||||||
"type": "deployment_environment",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"deployments_user_id_users_id_fk": {
|
|
||||||
"name": "deployments_user_id_users_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "deployments_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "deployments_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"deployments_machine_id_machines_id_fk": {
|
|
||||||
"name": "deployments_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "deployments",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"deployments_share_slug_unique": {
|
|
||||||
"name": "deployments_share_slug_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"share_slug"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machines": {
|
|
||||||
"name": "machines",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"endpoint": {
|
|
||||||
"name": "endpoint",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"disabled": {
|
|
||||||
"name": "disabled",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"auth_token": {
|
|
||||||
"name": "auth_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"name": "type",
|
|
||||||
"type": "machine_type",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'classic'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "machine_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'ready'"
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"models": {
|
|
||||||
"name": "models",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"gpu": {
|
|
||||||
"name": "gpu",
|
|
||||||
"type": "machine_gpu",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_machine_instance_id": {
|
|
||||||
"name": "build_machine_instance_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"build_log": {
|
|
||||||
"name": "build_log",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"machines_user_id_users_id_fk": {
|
|
||||||
"name": "machines_user_id_users_id_fk",
|
|
||||||
"tableFrom": "machines",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"subscription_status": {
|
|
||||||
"name": "subscription_status",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"stripe_customer_id": {
|
|
||||||
"name": "stripe_customer_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"plan": {
|
|
||||||
"name": "plan",
|
|
||||||
"type": "subscription_plan",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "subscription_plan_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"subscription_id": {
|
|
||||||
"name": "subscription_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"subscription_item_plan_id": {
|
|
||||||
"name": "subscription_item_plan_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"subscription_item_api_id": {
|
|
||||||
"name": "subscription_item_api_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"cancel_at_period_end": {
|
|
||||||
"name": "cancel_at_period_end",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"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": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"user_usage": {
|
|
||||||
"name": "user_usage",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"usage_time": {
|
|
||||||
"name": "usage_time",
|
|
||||||
"type": "real",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"user_usage_user_id_users_id_fk": {
|
|
||||||
"name": "user_usage_user_id_users_id_fk",
|
|
||||||
"tableFrom": "user_usage",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_run_outputs": {
|
|
||||||
"name": "workflow_run_outputs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"run_id": {
|
|
||||||
"name": "run_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"name": "data",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
|
|
||||||
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
|
|
||||||
"tableFrom": "workflow_run_outputs",
|
|
||||||
"tableTo": "workflow_runs",
|
|
||||||
"columnsFrom": [
|
|
||||||
"run_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_runs": {
|
|
||||||
"name": "workflow_runs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_inputs": {
|
|
||||||
"name": "workflow_inputs",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"origin": {
|
|
||||||
"name": "origin",
|
|
||||||
"type": "workflow_run_origin",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'api'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "workflow_run_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'not-started'"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"started_at": {
|
|
||||||
"name": "started_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_machine_id_machines_id_fk": {
|
|
||||||
"name": "workflow_runs_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflows": {
|
|
||||||
"name": "workflows",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflows_user_id_users_id_fk": {
|
|
||||||
"name": "workflows_user_id_users_id_fk",
|
|
||||||
"tableFrom": "workflows",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_versions": {
|
|
||||||
"name": "workflow_versions",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow": {
|
|
||||||
"name": "workflow",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_api": {
|
|
||||||
"name": "workflow_api",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"name": "version",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_versions_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_versions_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_versions",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"deployment_environment": {
|
|
||||||
"name": "deployment_environment",
|
|
||||||
"values": {
|
|
||||||
"staging": "staging",
|
|
||||||
"production": "production",
|
|
||||||
"public-share": "public-share"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_gpu": {
|
|
||||||
"name": "machine_gpu",
|
|
||||||
"values": {
|
|
||||||
"T4": "T4",
|
|
||||||
"A10G": "A10G",
|
|
||||||
"A100": "A100"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_status": {
|
|
||||||
"name": "machine_status",
|
|
||||||
"values": {
|
|
||||||
"ready": "ready",
|
|
||||||
"building": "building",
|
|
||||||
"error": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_type": {
|
|
||||||
"name": "machine_type",
|
|
||||||
"values": {
|
|
||||||
"classic": "classic",
|
|
||||||
"runpod-serverless": "runpod-serverless",
|
|
||||||
"modal-serverless": "modal-serverless",
|
|
||||||
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subscription_plan": {
|
|
||||||
"name": "subscription_plan",
|
|
||||||
"values": {
|
|
||||||
"basic": "basic",
|
|
||||||
"pro": "pro",
|
|
||||||
"enterprise": "enterprise"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subscription_plan_status": {
|
|
||||||
"name": "subscription_plan_status",
|
|
||||||
"values": {
|
|
||||||
"active": "active",
|
|
||||||
"deleted": "deleted",
|
|
||||||
"paused": "paused"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -246,97 +246,6 @@
|
|||||||
"when": 1705902960991,
|
"when": 1705902960991,
|
||||||
"tag": "0034_even_lady_ursula",
|
"tag": "0034_even_lady_ursula",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 35,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1705981452482,
|
|
||||||
"tag": "0035_fearless_golden_guardian",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 36,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706081335387,
|
|
||||||
"tag": "0036_flippant_meltdown",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 37,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706086271525,
|
|
||||||
"tag": "0037_eager_cyclops",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 38,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706095853896,
|
|
||||||
"tag": "0038_yummy_darkhawk",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 39,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706109812261,
|
|
||||||
"tag": "0039_nostalgic_lyja",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 40,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706110859352,
|
|
||||||
"tag": "0040_salty_archangel",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 41,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706111421524,
|
|
||||||
"tag": "0041_thick_norrin_radd",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 42,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706164614659,
|
|
||||||
"tag": "0042_windy_madelyne_pryor",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 43,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706241449348,
|
|
||||||
"tag": "0043_wealthy_nicolaos",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 44,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706317908300,
|
|
||||||
"tag": "0044_panoramic_mister_fear",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 45,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706336448134,
|
|
||||||
"tag": "0045_careful_cerise",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 46,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706383154642,
|
|
||||||
"tag": "0046_complex_mentallo",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 47,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1706384528895,
|
|
||||||
"tag": "0047_gifted_starbolt",
|
|
||||||
"breakpoints": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -19,26 +19,23 @@
|
|||||||
"@algolia/autocomplete-core": "^1.13.0",
|
"@algolia/autocomplete-core": "^1.13.0",
|
||||||
"@aws-sdk/client-s3": "^3.472.0",
|
"@aws-sdk/client-s3": "^3.472.0",
|
||||||
"@aws-sdk/s3-request-presigner": "^3.472.0",
|
"@aws-sdk/s3-request-presigner": "^3.472.0",
|
||||||
"@clerk/nextjs": "4.29.5",
|
"@clerk/nextjs": "^4.29.3",
|
||||||
"@headlessui/react": "^1.7.17",
|
"@headlessui/react": "^1.7.17",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@hono/swagger-ui": "^0.2.1",
|
"@hono/swagger-ui": "^0.2.1",
|
||||||
"@hono/zod-openapi": "^0.9.5",
|
"@hono/zod-openapi": "^0.9.5",
|
||||||
"@hono/zod-validator": "^0.1.11",
|
"@hono/zod-validator": "^0.1.11",
|
||||||
"@hookform/resolvers": "^3.3.2",
|
"@hookform/resolvers": "^3.3.2",
|
||||||
"@lemonsqueezy/lemonsqueezy.js": "^1.2.5",
|
|
||||||
"@mdx-js/loader": "^3.0.0",
|
"@mdx-js/loader": "^3.0.0",
|
||||||
"@mdx-js/react": "^3.0.0",
|
"@mdx-js/react": "^3.0.0",
|
||||||
"@neondatabase/serverless": "^0.6.0",
|
"@neondatabase/serverless": "^0.6.0",
|
||||||
"@next/mdx": "^14.0.4",
|
"@next/mdx": "^14.0.4",
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
"@radix-ui/react-progress": "^1.0.3",
|
|
||||||
"@radix-ui/react-radio-group": "^1.1.3",
|
"@radix-ui/react-radio-group": "^1.1.3",
|
||||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||||
"@radix-ui/react-select": "^2.0.0",
|
"@radix-ui/react-select": "^2.0.0",
|
||||||
@@ -49,7 +46,6 @@
|
|||||||
"@radix-ui/react-toggle": "^1.0.3",
|
"@radix-ui/react-toggle": "^1.0.3",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
"@sindresorhus/slugify": "^2.2.1",
|
"@sindresorhus/slugify": "^2.2.1",
|
||||||
"@stripe/stripe-js": "^2.4.0",
|
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@tanstack/react-table": "^8.10.7",
|
"@tanstack/react-table": "^8.10.7",
|
||||||
"@tanstack/react-virtual": "beta",
|
"@tanstack/react-virtual": "beta",
|
||||||
@@ -98,7 +94,6 @@
|
|||||||
"shikiji": "^0.9.3",
|
"shikiji": "^0.9.3",
|
||||||
"simple-functional-loader": "^1.2.1",
|
"simple-functional-loader": "^1.2.1",
|
||||||
"sonner": "^1.2.4",
|
"sonner": "^1.2.4",
|
||||||
"stripe": "^14.13.0",
|
|
||||||
"swagger-ui-react": "^5.11.0",
|
"swagger-ui-react": "^5.11.0",
|
||||||
"swr": "^2.2.4",
|
"swr": "^2.2.4",
|
||||||
"tailwind-merge": "^2.1.0",
|
"tailwind-merge": "^2.1.0",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 29 KiB |
@@ -1,175 +0,0 @@
|
|||||||
import { auth, clerkClient } from "@clerk/nextjs";
|
|
||||||
import { headers } from "next/headers";
|
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { stripe } from "../../../../../server/stripe";
|
|
||||||
import { NextResponse } from "next/server";
|
|
||||||
import { db } from "@/db/db";
|
|
||||||
import { subscriptionStatusTable } from "@/db/schema";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
|
|
||||||
const secret = process.env.STRIPE_WEBHOOK_SECRET || "";
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
try {
|
|
||||||
const body = await req.text();
|
|
||||||
|
|
||||||
const signature = headers().get("stripe-signature");
|
|
||||||
|
|
||||||
if (!signature)
|
|
||||||
return new NextResponse("Signature not found.", {
|
|
||||||
status: 500,
|
|
||||||
});
|
|
||||||
|
|
||||||
const event = stripe.webhooks.constructEvent(body, signature, secret);
|
|
||||||
|
|
||||||
console.log(event);
|
|
||||||
|
|
||||||
if (event.type === "checkout.session.completed") {
|
|
||||||
if (!event.data.object.customer_details?.email) {
|
|
||||||
throw new Error(`missing user email, ${event.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userId = event.data.object.metadata?.userId;
|
|
||||||
const plan = event.data.object.metadata?.plan;
|
|
||||||
if (!userId) {
|
|
||||||
throw new Error(`missing itinerary_id on metadata, ${event.id}`);
|
|
||||||
}
|
|
||||||
if (!plan) {
|
|
||||||
throw new Error(`missing plan on metadata, ${event.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const orgId = event.data.object.metadata?.orgId;
|
|
||||||
|
|
||||||
const customerId =
|
|
||||||
typeof event.data.object.customer == "string"
|
|
||||||
? event.data.object.customer
|
|
||||||
: event.data.object.customer?.id;
|
|
||||||
const subscriptionId =
|
|
||||||
typeof event.data.object.subscription == "string"
|
|
||||||
? event.data.object.subscription
|
|
||||||
: event.data.object.subscription?.id;
|
|
||||||
|
|
||||||
if (!customerId) {
|
|
||||||
throw new Error(`missing customerId, ${event.id}`);
|
|
||||||
}
|
|
||||||
if (!subscriptionId) {
|
|
||||||
throw new Error(`missing subscriptionId, ${event.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const items = await stripe.subscriptionItems.list({
|
|
||||||
subscription: subscriptionId,
|
|
||||||
limit: 5,
|
|
||||||
});
|
|
||||||
|
|
||||||
// getting the subscription item id for the api plan
|
|
||||||
const subscription_item_api_id = items.data.find(
|
|
||||||
(x) => x.price.id === process.env.STRIPE_PR_API,
|
|
||||||
)?.id;
|
|
||||||
|
|
||||||
// the plan could be either pro or enterprise
|
|
||||||
const subscription_item_plan_id =
|
|
||||||
items.data.find((x) => x.price.id === process.env.STRIPE_PR_PRO)?.id ??
|
|
||||||
items.data.find((x) => x.price.id === process.env.STRIPE_PR_ENTERPRISE)
|
|
||||||
?.id;
|
|
||||||
|
|
||||||
if (!subscription_item_api_id) {
|
|
||||||
throw new Error(
|
|
||||||
`missing plan on subscription_item_api_id, ${event.id}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!subscription_item_plan_id) {
|
|
||||||
throw new Error(
|
|
||||||
`missing plan on subscription_item_plan_id, ${event.id}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(items);
|
|
||||||
|
|
||||||
await db
|
|
||||||
.insert(subscriptionStatusTable)
|
|
||||||
.values({
|
|
||||||
stripe_customer_id: customerId,
|
|
||||||
org_id: orgId && orgId.length > 0 && orgId != "null" ? orgId : null,
|
|
||||||
user_id: userId,
|
|
||||||
subscription_id: subscriptionId,
|
|
||||||
plan: plan as "pro" | "enterprise" | "basic",
|
|
||||||
status: "active",
|
|
||||||
subscription_item_api_id: subscription_item_api_id,
|
|
||||||
subscription_item_plan_id: subscription_item_plan_id,
|
|
||||||
})
|
|
||||||
.onConflictDoNothing();
|
|
||||||
|
|
||||||
// updateDatabase(event.data.object.metadata.itinerary_id);
|
|
||||||
// sendEmail(event.data.object.customer_details.email);
|
|
||||||
} else if (event.type === "customer.subscription.paused") {
|
|
||||||
const customerId =
|
|
||||||
typeof event.data.object.customer == "string"
|
|
||||||
? event.data.object.customer
|
|
||||||
: event.data.object.customer?.id;
|
|
||||||
|
|
||||||
if (!customerId) {
|
|
||||||
throw new Error(`missing customerId, ${event.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await db
|
|
||||||
.update(subscriptionStatusTable)
|
|
||||||
.set({ status: "paused" })
|
|
||||||
.where(eq(subscriptionStatusTable.stripe_customer_id, customerId));
|
|
||||||
} else if (event.type === "customer.subscription.resumed") {
|
|
||||||
const customerId =
|
|
||||||
typeof event.data.object.customer == "string"
|
|
||||||
? event.data.object.customer
|
|
||||||
: event.data.object.customer?.id;
|
|
||||||
|
|
||||||
if (!customerId) {
|
|
||||||
throw new Error(`missing customerId, ${event.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await db
|
|
||||||
.update(subscriptionStatusTable)
|
|
||||||
.set({ status: "active" })
|
|
||||||
.where(eq(subscriptionStatusTable.stripe_customer_id, customerId));
|
|
||||||
} else if (event.type === "customer.subscription.deleted") {
|
|
||||||
const customerId =
|
|
||||||
typeof event.data.object.customer == "string"
|
|
||||||
? event.data.object.customer
|
|
||||||
: event.data.object.customer?.id;
|
|
||||||
|
|
||||||
if (!customerId) {
|
|
||||||
throw new Error(`missing customerId, ${event.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await db
|
|
||||||
.update(subscriptionStatusTable)
|
|
||||||
.set({ status: "deleted" })
|
|
||||||
.where(eq(subscriptionStatusTable.stripe_customer_id, customerId));
|
|
||||||
} else if (event.type === "customer.subscription.updated") {
|
|
||||||
const customerId =
|
|
||||||
typeof event.data.object.customer == "string"
|
|
||||||
? event.data.object.customer
|
|
||||||
: event.data.object.customer?.id;
|
|
||||||
|
|
||||||
if (!customerId) {
|
|
||||||
throw new Error(`missing customerId, ${event.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancel_at_period_end = event.data.object.cancel_at_period_end;
|
|
||||||
|
|
||||||
await db
|
|
||||||
.update(subscriptionStatusTable)
|
|
||||||
.set({ cancel_at_period_end: cancel_at_period_end })
|
|
||||||
.where(eq(subscriptionStatusTable.stripe_customer_id, customerId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json({ result: event, ok: true });
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
message: `Something went wrong: ${error}`,
|
|
||||||
ok: false,
|
|
||||||
},
|
|
||||||
{ status: 500 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { stripe } from "@/server/stripe";
|
|
||||||
import { auth, clerkClient } from "@clerk/nextjs";
|
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { getUrlServerSide } from "@/server/getUrlServerSide";
|
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
|
||||||
const { userId, orgId } = auth();
|
|
||||||
|
|
||||||
const plan = new URL(req.url).searchParams.get("plan");
|
|
||||||
|
|
||||||
if (!userId) return redirect("/");
|
|
||||||
if (!plan) return redirect("/pricing");
|
|
||||||
|
|
||||||
const user = await clerkClient.users.getUser(userId);
|
|
||||||
|
|
||||||
const mapping = {
|
|
||||||
pro: process.env.STRIPE_PR_PRO,
|
|
||||||
enterprise: process.env.STRIPE_PR_ENTERPRISE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const api = process.env.STRIPE_PR_API;
|
|
||||||
|
|
||||||
const session = await stripe.checkout.sessions.create({
|
|
||||||
success_url: getUrlServerSide(),
|
|
||||||
line_items: [
|
|
||||||
{
|
|
||||||
price: mapping[plan as "pro" | "enterprise"],
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
price: api,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
metadata: {
|
|
||||||
userId: userId,
|
|
||||||
orgId: orgId ?? null,
|
|
||||||
plan: plan,
|
|
||||||
},
|
|
||||||
allow_promotion_codes: true,
|
|
||||||
client_reference_id: orgId ?? userId,
|
|
||||||
customer_email: user.emailAddresses[0].emailAddress,
|
|
||||||
mode: "subscription",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (session.url) redirect(session.url);
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { stripe } from "@/server/stripe";
|
|
||||||
import { auth } from "@clerk/nextjs";
|
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { getUrlServerSide } from "@/server/getUrlServerSide";
|
|
||||||
import { getCurrentPlan } from "@/server/getCurrentPlan";
|
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
|
||||||
const { userId, orgId } = auth();
|
|
||||||
|
|
||||||
if (!userId) return redirect("/");
|
|
||||||
|
|
||||||
const change = new URL(req.url).searchParams.get("change");
|
|
||||||
|
|
||||||
const sub = await getCurrentPlan({
|
|
||||||
org_id: orgId,
|
|
||||||
user_id: userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!sub) return redirect("/pricing");
|
|
||||||
|
|
||||||
const session = await stripe.billingPortal.sessions.create({
|
|
||||||
customer: sub.stripe_customer_id,
|
|
||||||
return_url: getUrlServerSide() + "/pricing",
|
|
||||||
// flow_data:
|
|
||||||
// change === "true" && sub.subscription_id
|
|
||||||
// ? {
|
|
||||||
// type: "subscription_update",
|
|
||||||
// subscription_update: {
|
|
||||||
// subscription: sub.subscription_id,
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// : undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
redirect(session.url);
|
|
||||||
// if (session.url) redirect(session.url);
|
|
||||||
}
|
|
||||||
@@ -1,129 +1,56 @@
|
|||||||
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import {
|
import { workflowRunOutputs, workflowRunsTable } from "@/db/schema";
|
||||||
WorkflowRunStatusSchema,
|
|
||||||
userUsageTable,
|
|
||||||
workflowRunOutputs,
|
|
||||||
workflowRunsTable,
|
|
||||||
workflowTable,
|
|
||||||
} from "@/db/schema";
|
|
||||||
import { getCurrentPlan } from "@/server/getCurrentPlan";
|
|
||||||
import { stripe } from "@/server/stripe";
|
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const Request = z.object({
|
const Request = z.object({
|
||||||
run_id: z.string(),
|
run_id: z.string(),
|
||||||
status: WorkflowRunStatusSchema.optional(),
|
status: z
|
||||||
time: z.coerce.date().optional(),
|
.enum(["not-started", "running", "uploading", "success", "failed"])
|
||||||
|
.optional(),
|
||||||
output_data: z.any().optional(),
|
output_data: z.any().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
const [data, error] = await parseDataSafe(Request, request);
|
||||||
const [data, error] = await parseDataSafe(Request, request);
|
if (!data || error) return error;
|
||||||
|
|
||||||
if (!data || error) return error;
|
const { run_id, status, output_data } = data;
|
||||||
|
|
||||||
const { run_id, status, time, output_data } = data;
|
// console.log(run_id, status, output_data);
|
||||||
|
|
||||||
if (status == "started" && time != undefined) {
|
if (output_data) {
|
||||||
// It successfully started, update the started_at time
|
const workflow_run_output = await db.insert(workflowRunOutputs).values({
|
||||||
await db
|
run_id: run_id,
|
||||||
.update(workflowRunsTable)
|
data: output_data,
|
||||||
.set({
|
});
|
||||||
started_at: time,
|
} else if (status) {
|
||||||
})
|
// console.log("status", status);
|
||||||
.where(eq(workflowRunsTable.id, run_id));
|
const workflow_run = await db
|
||||||
}
|
.update(workflowRunsTable)
|
||||||
|
.set({
|
||||||
if (status == "queued" && time != undefined) {
|
status: status,
|
||||||
// It successfully started, update the started_at time
|
ended_at:
|
||||||
await db
|
status === "success" || status === "failed" ? new Date() : null,
|
||||||
.update(workflowRunsTable)
|
})
|
||||||
.set({
|
.where(eq(workflowRunsTable.id, run_id))
|
||||||
queued_at: time,
|
.returning();
|
||||||
})
|
|
||||||
.where(eq(workflowRunsTable.id, run_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output_data) {
|
|
||||||
const workflow_run_output = await db.insert(workflowRunOutputs).values({
|
|
||||||
run_id: run_id,
|
|
||||||
data: output_data,
|
|
||||||
});
|
|
||||||
} else if (status) {
|
|
||||||
const [workflow_run] = await db
|
|
||||||
.update(workflowRunsTable)
|
|
||||||
.set({
|
|
||||||
status: status,
|
|
||||||
ended_at:
|
|
||||||
status === "success" || status === "failed" ? new Date() : null,
|
|
||||||
})
|
|
||||||
.where(eq(workflowRunsTable.id, run_id))
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
// Need to filter out only comfy deploy serverless
|
|
||||||
// Also multiply with the gpu selection
|
|
||||||
if (workflow_run.machine_type == "comfy-deploy-serverless") {
|
|
||||||
if (
|
|
||||||
(status === "success" || status === "failed") &&
|
|
||||||
workflow_run.user_id
|
|
||||||
) {
|
|
||||||
const sub = await getCurrentPlan({
|
|
||||||
user_id: workflow_run.user_id,
|
|
||||||
org_id: workflow_run.org_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (sub && sub.subscription_item_api_id && workflow_run.ended_at) {
|
|
||||||
let durationInSec = Math.abs(
|
|
||||||
(workflow_run.ended_at.getTime() -
|
|
||||||
workflow_run.created_at.getTime()) /
|
|
||||||
1000,
|
|
||||||
);
|
|
||||||
durationInSec = Math.ceil(durationInSec);
|
|
||||||
switch (workflow_run.gpu) {
|
|
||||||
case "A100":
|
|
||||||
durationInSec *= 7;
|
|
||||||
break;
|
|
||||||
case "A10G":
|
|
||||||
durationInSec *= 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
await stripe.subscriptionItems.createUsageRecord(
|
|
||||||
sub.subscription_item_api_id,
|
|
||||||
{
|
|
||||||
quantity: durationInSec,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
message: "success",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
console.log("An error here");
|
|
||||||
|
|
||||||
const errorMessage =
|
|
||||||
error instanceof Error ? error.message : "Unknown error";
|
|
||||||
console.log(errorMessage);
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
error: errorMessage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 500,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const workflow_version = await db.query.workflowVersionTable.findFirst({
|
||||||
|
// where: eq(workflowRunsTable.id, workflow_run[0].workflow_version_id),
|
||||||
|
// });
|
||||||
|
|
||||||
|
// revalidatePath(`./${workflow_version?.workflow_id}`);
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
message: "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
|
||||||
import { db } from "@/db/db";
|
|
||||||
import { modelTable } from "@/db/schema";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { NextResponse } from "next/server";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const Request = z.object({
|
|
||||||
volume_id: z.string(),
|
|
||||||
model_id: z.string(),
|
|
||||||
folder_path: z.string().optional(),
|
|
||||||
status: z.enum(["success", "failed"]),
|
|
||||||
error_log: z.string().optional(),
|
|
||||||
timeout: z.number().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const [data, error] = await parseDataSafe(Request, request);
|
|
||||||
if (!data || error) return error;
|
|
||||||
|
|
||||||
const { model_id, error_log, status, folder_path } = data;
|
|
||||||
console.log(model_id, error_log, status, folder_path);
|
|
||||||
|
|
||||||
if (status === "success") {
|
|
||||||
await db
|
|
||||||
.update(modelTable)
|
|
||||||
.set({
|
|
||||||
status: "success",
|
|
||||||
folder_path,
|
|
||||||
updated_at: new Date(),
|
|
||||||
// build_log: build_log,
|
|
||||||
})
|
|
||||||
.where(eq(modelTable.id, model_id));
|
|
||||||
} else {
|
|
||||||
await db
|
|
||||||
.update(modelTable)
|
|
||||||
.set({
|
|
||||||
status: "failed",
|
|
||||||
error_log,
|
|
||||||
updated_at: new Date(),
|
|
||||||
// status: "error",
|
|
||||||
// build_log: build_log,
|
|
||||||
})
|
|
||||||
.where(eq(modelTable.id, model_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
message: "success",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { SignIn } from "@clerk/nextjs";
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
return (
|
|
||||||
<div className="w-full h-full items-center flex justify-center">
|
|
||||||
<SignIn />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { SignUp } from "@clerk/nextjs";
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
return (
|
|
||||||
<div className="w-full h-full items-center flex justify-center">
|
|
||||||
<SignUp />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return <Examples />;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type exampleWorkflow = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
previewURL: string;
|
||||||
|
image: {
|
||||||
|
src: string,
|
||||||
|
alt: string,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const exampleWorkflows: exampleWorkflow[] = [
|
||||||
|
{
|
||||||
|
title: "Txt2Img SDXL",
|
||||||
|
description: "The basic workflow, type a prompt and generate images based on that.",
|
||||||
|
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-txt2img-sdxl',
|
||||||
|
image: {
|
||||||
|
src: '/example-workflows/txt2img.webp',
|
||||||
|
alt: 'IPAdapter workflow',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Txt2Img LCM SDXL",
|
||||||
|
description: "Images in a couple of seconds, increase the speed of each generation using LCM Lora.",
|
||||||
|
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-lcm-sdxl',
|
||||||
|
image: {
|
||||||
|
src: '/example-workflows/txt2img-lcm.webp',
|
||||||
|
alt: 'txt2img LCM SDXL',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "IPAdapter SDXL",
|
||||||
|
description: "Load images and use them as reference for new generations.",
|
||||||
|
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-ip-adapter-sdxl',
|
||||||
|
image: {
|
||||||
|
src: '/example-workflows/ipadapter.webp',
|
||||||
|
alt: 'IPAdapter workflow',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Upscale and Add Detail SDXL",
|
||||||
|
description: "Upscale and Add Details to your creations.",
|
||||||
|
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-upscale-and-add-detail-sdxl',
|
||||||
|
image: {
|
||||||
|
src: '/example-workflows/upscale.webp',
|
||||||
|
alt: 'Upscale and Add Detail SDXL',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Txt2Img SDXL Turbo",
|
||||||
|
description: "Try SDXL turbo and generate images since 1 step in seconds.",
|
||||||
|
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-txt2img-sdxl-turbo',
|
||||||
|
image: {
|
||||||
|
src: '/example-workflows/txt2img-sdxl-turbo.webp',
|
||||||
|
alt: 'Txt2Img SDXL Turbo',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Img2Img SDXL Controlnet",
|
||||||
|
description: "This workflow uses canny. Generate lines of you original image and create variations.",
|
||||||
|
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-img2-img-sdxl-controlnet',
|
||||||
|
image: {
|
||||||
|
src: '/example-workflows/txt2img-controlnet.webp',
|
||||||
|
alt: 'Img2Img SDXL Controlnet',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Automatic Inpainting (SEG)",
|
||||||
|
description: "Type what do you want to select and change that area with your prompt.",
|
||||||
|
previewURL: 'https://www.comfydeploy.com/share/comfy-deploy-example-automatic-inpainting-clip-seg',
|
||||||
|
image: {
|
||||||
|
src: '/example-workflows/automatic-inpainting-seg.webp',
|
||||||
|
alt: 'Img2Img SDXL Controlnet',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
async function Examples() {
|
||||||
|
return (
|
||||||
|
<div className="w-full py-4">
|
||||||
|
<section className="mx-auto flex max-w-[980px] flex-col items-center gap-2 py-8 md:py-12 md:pb-8 lg:py-24 lg:pb-20">
|
||||||
|
<h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl text-center">
|
||||||
|
Check out some examples
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-[560px] text-center text-lg text-muted-foreground">Text to Image, Image to Image, IPAdapter, and more. Here are some examples that you can use to deploy your workflow.</p>
|
||||||
|
</section>
|
||||||
|
<section className="flex justify-center flex-wrap gap-5">
|
||||||
|
{exampleWorkflows.map(workflow => {
|
||||||
|
return <Card className="w-[350px]">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{workflow.title}</CardTitle>
|
||||||
|
<CardDescription>{workflow.description}</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Image src={workflow.image.src} alt={workflow.image.alt} width={350} height={230} />
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex justify-end gap-2">
|
||||||
|
<Button asChild>
|
||||||
|
<Link href={workflow.previewURL}>View Workflow</Link>
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>;
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -80,7 +80,9 @@
|
|||||||
/* @apply rounded-lg p-2 overflow-x-scroll */
|
/* @apply rounded-lg p-2 overflow-x-scroll */
|
||||||
@apply p-2 max-w-full overflow-auto w-full
|
@apply p-2 max-w-full overflow-auto w-full
|
||||||
}
|
}
|
||||||
|
.vsc-controller{
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { AccessType } from "../../../lib/AccessType";
|
|||||||
import { MachineList } from "@/components/MachineList";
|
import { MachineList } from "@/components/MachineList";
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import { machinesTable } from "@/db/schema";
|
import { machinesTable } from "@/db/schema";
|
||||||
import { getCurrentPlanWithAuth } from "@/server/getCurrentPlan";
|
|
||||||
import { auth } from "@clerk/nextjs";
|
import { auth } from "@clerk/nextjs";
|
||||||
import { clerkClient } from "@clerk/nextjs/server";
|
import { clerkClient } from "@clerk/nextjs/server";
|
||||||
import { desc, eq, isNull, and } from "drizzle-orm";
|
import { desc, eq, isNull, and } from "drizzle-orm";
|
||||||
@@ -20,8 +19,6 @@ async function MachineListServer() {
|
|||||||
|
|
||||||
const user = await clerkClient.users.getUser(userId);
|
const user = await clerkClient.users.getUser(userId);
|
||||||
|
|
||||||
const sub = await getCurrentPlanWithAuth();
|
|
||||||
|
|
||||||
const machines = await db.query.machinesTable.findMany({
|
const machines = await db.query.machinesTable.findMany({
|
||||||
orderBy: desc(machinesTable.updated_at),
|
orderBy: desc(machinesTable.updated_at),
|
||||||
where:
|
where:
|
||||||
@@ -34,7 +31,6 @@ async function MachineListServer() {
|
|||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{/* <div>Machines</div> */}
|
{/* <div>Machines</div> */}
|
||||||
<MachineList
|
<MachineList
|
||||||
sub={sub}
|
|
||||||
data={machines}
|
data={machines}
|
||||||
userMetadata={AccessType.parse(user.privateMetadata ?? {})}
|
userMetadata={AccessType.parse(user.privateMetadata ?? {})}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import PricingList from "@/components/PricingPlan";
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<PricingList />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
// app/providers.tsx
|
// app/providers.tsx
|
||||||
"use client";
|
'use client'
|
||||||
import posthog from "posthog-js";
|
import posthog from 'posthog-js'
|
||||||
import { PostHogProvider } from "posthog-js/react";
|
import { PostHogProvider } from 'posthog-js/react'
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== 'undefined') {
|
||||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
||||||
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
||||||
capture_pageview: false, // Disable automatic pageview capture, as we capture manually
|
capture_pageview: false // Disable automatic pageview capture, as we capture manually
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PHProvider({
|
export function PHProvider({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
|
return <PostHogProvider client={posthog}>{children}</PostHogProvider>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
"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()} />;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { setInitialUserData } from "../../../lib/setInitialUserData";
|
|
||||||
import { auth } from "@clerk/nextjs";
|
|
||||||
import { clerkClient } from "@clerk/nextjs/server";
|
|
||||||
import { ModelList } from "@/components/ModelList";
|
|
||||||
import { getAllUserModels } from "@/server/getAllUserModel";
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
return <ModelListServer />;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ModelListServer() {
|
|
||||||
const { userId } = auth();
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
return <div>No auth</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await clerkClient.users.getUser(userId);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
await setInitialUserData(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const models = await getAllUserModels();
|
|
||||||
|
|
||||||
if (!models) {
|
|
||||||
return <div>No models found</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full">
|
|
||||||
<ModelList data={models} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
"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()} />;
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import PricingList from "@/components/PricingPlan";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import { Progress } from "@/components/ui/progress";
|
|
||||||
import { getCurrentPlanWithAuth } from "@/server/getCurrentPlan";
|
|
||||||
import { stripe } from "@/server/stripe";
|
|
||||||
|
|
||||||
const freeTierSeconds = 30000;
|
|
||||||
|
|
||||||
export default async function Home() {
|
|
||||||
const sub = await getCurrentPlanWithAuth();
|
|
||||||
|
|
||||||
const data = sub?.subscription_item_api_id
|
|
||||||
? await stripe.subscriptionItems.listUsageRecordSummaries(
|
|
||||||
sub?.subscription_item_api_id,
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mt-4 flex items-center justify-center">
|
|
||||||
<Card className="p-4 w-full max-w-[600px]">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Account Usage</CardTitle>
|
|
||||||
<CardDescription>View you account usage</CardDescription>
|
|
||||||
<Badge className="w-fit">{sub?.plan}</Badge>
|
|
||||||
</CardHeader>
|
|
||||||
{data && (
|
|
||||||
<CardContent className="text-sm flex flex-col gap-2">
|
|
||||||
<div className="flex justify-between gap-2">
|
|
||||||
<span>Current free gpu usage:</span>
|
|
||||||
{
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Badge>
|
|
||||||
{data.data[0].total_usage}s /{Math.floor(freeTierSeconds)}s
|
|
||||||
</Badge>
|
|
||||||
<Badge>
|
|
||||||
{Math.floor(data.data[0].total_usage / 60 / 60)}hr /
|
|
||||||
{Math.floor(freeTierSeconds / 60 / 60)}hr
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<Progress
|
|
||||||
value={(data.data[0].total_usage / freeTierSeconds) * 100}
|
|
||||||
></Progress>
|
|
||||||
</CardContent>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -21,8 +21,6 @@ import { getRelativeTime } from "@/lib/getRelativeTime";
|
|||||||
import { getMachines } from "@/server/curdMachine";
|
import { getMachines } from "@/server/curdMachine";
|
||||||
import { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
import { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
||||||
|
|
||||||
export const maxDuration = 300; // 5 minutes
|
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
export const maxDuration = 300; // 5 minutes
|
|
||||||
|
|
||||||
export default async function Layout({
|
export default async function Layout({
|
||||||
children,
|
children,
|
||||||
deployment,
|
deployment,
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { usersTable } from "@/db/schema";
|
|||||||
import { auth } from "@clerk/nextjs";
|
import { auth } from "@clerk/nextjs";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
export const maxDuration = 300; // 5 minutes
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return <WorkflowServer />;
|
return <WorkflowServer />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ import type { UnknownKeysParam, ZodObject, ZodRawShape, z } from "zod";
|
|||||||
export function InsertModal<
|
export function InsertModal<
|
||||||
K extends ZodRawShape,
|
K extends ZodRawShape,
|
||||||
Y extends UnknownKeysParam,
|
Y extends UnknownKeysParam,
|
||||||
Z extends ZodObject<K, Y>,
|
Z extends ZodObject<K, Y>
|
||||||
>(props: {
|
>(props: {
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
title: React.ReactNode;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
dialogClassName?: string;
|
dialogClassName?: string;
|
||||||
serverAction: (data: z.infer<Z>) => Promise<unknown>;
|
serverAction: (data: z.infer<Z>) => Promise<unknown>;
|
||||||
@@ -105,7 +105,7 @@ export function InsertModal<
|
|||||||
export function UpdateModal<
|
export function UpdateModal<
|
||||||
K extends ZodRawShape,
|
K extends ZodRawShape,
|
||||||
Y extends UnknownKeysParam,
|
Y extends UnknownKeysParam,
|
||||||
Z extends ZodObject<K, Y>,
|
Z extends ZodObject<K, Y>
|
||||||
>(props: {
|
>(props: {
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
setOpen?: (open: boolean) => void;
|
setOpen?: (open: boolean) => void;
|
||||||
@@ -118,7 +118,7 @@ export function UpdateModal<
|
|||||||
serverAction: (
|
serverAction: (
|
||||||
data: z.infer<Z> & {
|
data: z.infer<Z> & {
|
||||||
id: string;
|
id: string;
|
||||||
},
|
}
|
||||||
) => Promise<unknown>;
|
) => Promise<unknown>;
|
||||||
formSchema: Z;
|
formSchema: Z;
|
||||||
fieldConfig?: FieldConfig<z.infer<Z>>;
|
fieldConfig?: FieldConfig<z.infer<Z>>;
|
||||||
@@ -165,7 +165,7 @@ export function UpdateModal<
|
|||||||
props.serverAction({
|
props.serverAction({
|
||||||
...data,
|
...data,
|
||||||
id: props.data.id,
|
id: props.data.id,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|||||||
@@ -6,19 +6,6 @@ import { getConnectionStatus } from "@/components/getConnectionStatus";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import useWebSocket from "react-use-websocket";
|
import useWebSocket from "react-use-websocket";
|
||||||
|
|
||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog"
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
|
|
||||||
export function MachineBuildLog({
|
export function MachineBuildLog({
|
||||||
machine_id,
|
machine_id,
|
||||||
endpoint,
|
endpoint,
|
||||||
@@ -30,7 +17,6 @@ export function MachineBuildLog({
|
|||||||
}) {
|
}) {
|
||||||
const [logs, setLogs] = useState<LogsType>([]);
|
const [logs, setLogs] = useState<LogsType>([]);
|
||||||
const [finished, setFinished] = useState(false);
|
const [finished, setFinished] = useState(false);
|
||||||
const [status, setStatus] = useState<"failed" | "succuss">();
|
|
||||||
|
|
||||||
const wsEndpoint = endpoint.replace(/^http/, "ws");
|
const wsEndpoint = endpoint.replace(/^http/, "ws");
|
||||||
const query = { fly_instance_id: instance_id };
|
const query = { fly_instance_id: instance_id };
|
||||||
@@ -57,59 +43,13 @@ export function MachineBuildLog({
|
|||||||
setLogs((logs) => [...(logs ?? []), message.data]);
|
setLogs((logs) => [...(logs ?? []), message.data]);
|
||||||
} else if (message?.event === "FINISHED") {
|
} else if (message?.event === "FINISHED") {
|
||||||
setFinished(true);
|
setFinished(true);
|
||||||
setStatus(message.data.status)
|
|
||||||
}
|
}
|
||||||
}, [lastMessage]);
|
}, [lastMessage]);
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{connectionStatus}
|
{connectionStatus}
|
||||||
<LogsViewer logs={logs} />
|
<LogsViewer logs={logs} />
|
||||||
|
|
||||||
<AlertDialog open={finished}>
|
|
||||||
<AlertDialogContent>
|
|
||||||
{
|
|
||||||
status == "succuss" ? (
|
|
||||||
<>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle>Machine Built</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
Your machine is built, you can now integrate your API, or directly run to check this machines.
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogAction onClick={() => {
|
|
||||||
router.push("/workflows")
|
|
||||||
}}>See Workflows</AlertDialogAction>
|
|
||||||
<AlertDialogAction onClick={() => {
|
|
||||||
router.push("/machines")
|
|
||||||
}}>See All Machines</AlertDialogAction>
|
|
||||||
</AlertDialogFooter></>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle>Machine Failed</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
Something went wrong with the machine build, please check the log.
|
|
||||||
Possible cause could be conflits with custom nodes, build got stuck, timeout, or too many custom nodes installed.
|
|
||||||
Please attempt a rebuild or remove some of the custom nodes.
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogCancel>See logs</AlertDialogCancel>
|
|
||||||
<AlertDialogAction onClick={() => {
|
|
||||||
router.push("/machines")
|
|
||||||
}}>Back to machines</AlertDialogAction>
|
|
||||||
</AlertDialogFooter></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ import {
|
|||||||
updateCustomMachine,
|
updateCustomMachine,
|
||||||
updateMachine,
|
updateMachine,
|
||||||
} from "@/server/curdMachine";
|
} from "@/server/curdMachine";
|
||||||
import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
|
|
||||||
import { getCurrentPlanWithAuth } from "@/server/getCurrentPlan";
|
|
||||||
import type {
|
import type {
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
@@ -56,10 +54,9 @@ import {
|
|||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import { ArrowUpDown, Lock, MoreHorizontal, Plus } from "lucide-react";
|
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
export type Machine = MachineType;
|
export type Machine = MachineType;
|
||||||
@@ -116,18 +113,16 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
{row.original.status} <LoadingIcon />
|
{row.original.status} <LoadingIcon />
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{!row.original.disabled &&
|
{!row.original.disabled && row.original.status && (
|
||||||
row.original.status &&
|
<Badge
|
||||||
row.original.status != "building" && (
|
variant={
|
||||||
<Badge
|
row.original.status == "ready" ? "success" : "destructive"
|
||||||
variant={
|
}
|
||||||
row.original.status == "ready" ? "success" : "destructive"
|
className="capitalize"
|
||||||
}
|
>
|
||||||
className="capitalize"
|
{row.original.status}
|
||||||
>
|
</Badge>
|
||||||
{row.original.status}
|
)}
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
// </a>
|
// </a>
|
||||||
);
|
);
|
||||||
@@ -197,9 +192,7 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="text-destructive"
|
className="text-destructive"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
callServerPromise(deleteMachine(machine.id), {
|
callServerPromise(deleteMachine(machine.id));
|
||||||
loadingText: "Deleting machine",
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete Machine
|
Delete Machine
|
||||||
@@ -207,9 +200,7 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
{machine.disabled ? (
|
{machine.disabled ? (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
callServerPromise(enableMachine(machine.id), {
|
callServerPromise(enableMachine(machine.id));
|
||||||
loadingText: "Enabling machine",
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Enable Machine
|
Enable Machine
|
||||||
@@ -218,9 +209,7 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="text-destructive"
|
className="text-destructive"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
callServerPromise(disableMachine(machine.id), {
|
callServerPromise(disableMachine(machine.id));
|
||||||
loadingText: "Disabling machine",
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Disable Machine
|
Disable Machine
|
||||||
@@ -228,33 +217,22 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
)}
|
)}
|
||||||
{machine.type === "comfy-deploy-serverless" && (
|
{machine.type === "comfy-deploy-serverless" && (
|
||||||
<>
|
<>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem asChild>
|
||||||
onClick={async () => {
|
<a
|
||||||
const url = await callServerPromise(
|
target="_blank"
|
||||||
editWorkflowOnMachine(machine.id),
|
href={machine.endpoint.replace(
|
||||||
{
|
"comfyui-api",
|
||||||
loadingText: "Getting machine url...",
|
"comfyui-app"
|
||||||
},
|
)} rel="noreferrer"
|
||||||
);
|
>
|
||||||
if (url && typeof url !== "object") {
|
Open ComfyUI
|
||||||
window.open(url, "_blank");
|
</a>
|
||||||
} else if (url && typeof url === "object" && url.error) {
|
|
||||||
console.error(url.error);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Open ComfyUI
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
callServerPromise(
|
buildMachine({
|
||||||
buildMachine({
|
id: machine.id,
|
||||||
id: machine.id,
|
});
|
||||||
}),
|
|
||||||
{
|
|
||||||
loadingText: "Starting machine build process",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Rebuild
|
Rebuild
|
||||||
@@ -322,15 +300,13 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
export function MachineList({
|
export function MachineList({
|
||||||
data,
|
data,
|
||||||
userMetadata,
|
userMetadata,
|
||||||
sub,
|
|
||||||
}: {
|
}: {
|
||||||
data: Machine[];
|
data: Machine[];
|
||||||
userMetadata: z.infer<typeof AccessType>;
|
userMetadata: z.infer<typeof AccessType>;
|
||||||
sub: Awaited<ReturnType<typeof getCurrentPlanWithAuth>>;
|
|
||||||
}) {
|
}) {
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
[],
|
[]
|
||||||
);
|
);
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
@@ -355,21 +331,6 @@ export function MachineList({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let machineMaxCount = 2;
|
|
||||||
|
|
||||||
// Temp fixes for machine count
|
|
||||||
if (userMetadata.betaFeaturesAccess) machineMaxCount = 5;
|
|
||||||
|
|
||||||
if (sub?.plan == "pro") {
|
|
||||||
machineMaxCount = 10;
|
|
||||||
} else if (sub?.plan == "enterprise") {
|
|
||||||
machineMaxCount = 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
const locked =
|
|
||||||
data.some((machine) => machine.type === "modal-serverless") &&
|
|
||||||
data.length >= machineMaxCount;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
@@ -384,17 +345,17 @@ export function MachineList({
|
|||||||
<div className="ml-auto flex gap-2">
|
<div className="ml-auto flex gap-2">
|
||||||
<InsertModal
|
<InsertModal
|
||||||
dialogClassName="sm:max-w-[600px]"
|
dialogClassName="sm:max-w-[600px]"
|
||||||
disabled={locked}
|
disabled={
|
||||||
|
data.some(
|
||||||
|
(machine) => machine.type === "comfy-deploy-serverless"
|
||||||
|
) && !userMetadata.betaFeaturesAccess
|
||||||
|
}
|
||||||
tooltip={
|
tooltip={
|
||||||
locked
|
data.some((machine) => machine.type === "comfy-deploy-serverless")
|
||||||
? `Max ${machineMaxCount} ComfyUI machine for your account, upgrade to unlock more cnfiguration.`
|
? "Only one hosted machine at preview stage"
|
||||||
: `Max ${machineMaxCount} ComfyUI machine for your account`
|
: undefined
|
||||||
}
|
|
||||||
title={
|
|
||||||
<>
|
|
||||||
New Machine {locked ? <Lock size={14} /> : <Plus size={14} />}
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
title="New Machine"
|
||||||
description="Add custom ComfyUI machines to your account."
|
description="Add custom ComfyUI machines to your account."
|
||||||
serverAction={addCustomMachine}
|
serverAction={addCustomMachine}
|
||||||
formSchema={addCustomMachineSchema}
|
formSchema={addCustomMachineSchema}
|
||||||
@@ -449,7 +410,7 @@ export function MachineList({
|
|||||||
? null
|
? null
|
||||||
: flexRender(
|
: flexRender(
|
||||||
header.column.columnDef.header,
|
header.column.columnDef.header,
|
||||||
header.getContext(),
|
header.getContext()
|
||||||
)}
|
)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
);
|
||||||
@@ -468,7 +429,7 @@ export function MachineList({
|
|||||||
<TableCell key={cell.id}>
|
<TableCell key={cell.id}>
|
||||||
{flexRender(
|
{flexRender(
|
||||||
cell.column.columnDef.cell,
|
cell.column.columnDef.cell,
|
||||||
cell.getContext(),
|
cell.getContext()
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,412 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { getRelativeTime } from "../lib/getRelativeTime";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import { InsertModal } from "./InsertModal";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import type { getAllUserModels as getAllUserModels } from "@/server/getAllUserModel";
|
|
||||||
import type {
|
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
|
||||||
SortingState,
|
|
||||||
VisibilityState,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import {
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { ArrowUpDown } from "lucide-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import { addModel } from "@/server/curdModel";
|
|
||||||
import { downloadUrlModelSchema } from "@/server/addCivitaiModelSchema";
|
|
||||||
import { modelEnumType } from "@/db/schema";
|
|
||||||
|
|
||||||
export type ModelItemList = NonNullable<
|
|
||||||
Awaited<ReturnType<typeof getAllUserModels>>
|
|
||||||
>[0];
|
|
||||||
|
|
||||||
export const columns: ColumnDef<ModelItemList>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "id",
|
|
||||||
id: "select",
|
|
||||||
header: ({ table }) => (
|
|
||||||
<Checkbox
|
|
||||||
checked={table.getIsAllPageRowsSelected() ||
|
|
||||||
(table.getIsSomePageRowsSelected() && "indeterminate")}
|
|
||||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
|
||||||
aria-label="Select all"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<Checkbox
|
|
||||||
checked={row.getIsSelected()}
|
|
||||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
||||||
aria-label="Select row"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
enableSorting: false,
|
|
||||||
enableHiding: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "model_name",
|
|
||||||
header: ({ column }) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="flex items-center hover:underline"
|
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
|
||||||
>
|
|
||||||
Model Name
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const model = row.original;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{
|
|
||||||
/*<a
|
|
||||||
className="hover:underline flex gap-2"
|
|
||||||
href={`/storage/${model.id}`} // TODO
|
|
||||||
>*/
|
|
||||||
}
|
|
||||||
<span className="truncate max-w-[200px]">
|
|
||||||
{row.original.model_name}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{model.is_public ? <></> : <Badge variant="orange">Private</Badge>}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "status",
|
|
||||||
header: ({ column }) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="flex items-center hover:underline"
|
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<Badge
|
|
||||||
variant={row.original.status === "failed"
|
|
||||||
? "red"
|
|
||||||
: (row.original.status === "started" ? "yellow" : "green")}
|
|
||||||
>
|
|
||||||
{row.original.status}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
// NOTE: retry downloads on failures
|
|
||||||
// const oneHourAgo = new Date(new Date().getTime() - (60 * 60 * 1000));
|
|
||||||
// const lastUpdated = new Date(row.original.updated_at);
|
|
||||||
// const canRefresh = row.original.status === "failed" && lastUpdated < oneHourAgo;
|
|
||||||
// const canRefresh = row.original.status === "failed" && lastUpdated < oneHourAgo;
|
|
||||||
// cell: ({ row }) => {
|
|
||||||
// // const oneHourAgo = new Date(new Date().getTime() - (60 * 60 * 1000));
|
|
||||||
// // const lastUpdated = new Date(row.original.updated_at);
|
|
||||||
// // const canRefresh = row.original.status === "failed" && lastUpdated < oneHourAgo;
|
|
||||||
// const canReDownload = true;
|
|
||||||
//
|
|
||||||
// return (
|
|
||||||
// <div className="flex items-center space-x-2">
|
|
||||||
// <Badge
|
|
||||||
// variant={row.original.status === "failed"
|
|
||||||
// ? "red"
|
|
||||||
// : row.original.status === "started"
|
|
||||||
// ? "yellow"
|
|
||||||
// : "green"}
|
|
||||||
// >
|
|
||||||
// {row.original.status}
|
|
||||||
// </Badge>
|
|
||||||
// {canReDownload && (
|
|
||||||
// <RefreshCcw
|
|
||||||
// onClick={() => {
|
|
||||||
// redownloadCheckpoint(row.original);
|
|
||||||
// }}
|
|
||||||
// className="h-4 w-4 cursor-pointer" // Adjust the size with h-x and w-x classes
|
|
||||||
// />
|
|
||||||
// )}
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "upload_type",
|
|
||||||
header: ({ column }) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="flex items-center hover:underline"
|
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
|
||||||
>
|
|
||||||
Source
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return <Badge variant="cyan">{row.original.upload_type}</Badge>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "model_type",
|
|
||||||
header: ({ column }) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="flex items-center hover:underline"
|
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
|
||||||
>
|
|
||||||
Model Type
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const model_type_map: Record<modelEnumType, any> = {
|
|
||||||
"checkpoint": "amber",
|
|
||||||
"lora": "green",
|
|
||||||
"embedding": "violet",
|
|
||||||
"vae": "teal",
|
|
||||||
};
|
|
||||||
|
|
||||||
function getBadgeColor(modelType: modelEnumType) {
|
|
||||||
return model_type_map[modelType] || "default";
|
|
||||||
}
|
|
||||||
|
|
||||||
const color = getBadgeColor(row.original.model_type);
|
|
||||||
return <Badge variant={color}>{row.original.model_type}</Badge>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "date",
|
|
||||||
sortingFn: "datetime",
|
|
||||||
enableSorting: true,
|
|
||||||
header: ({ column }) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="w-full flex items-center justify-end hover:underline truncate"
|
|
||||||
// variant="ghost"
|
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
|
||||||
>
|
|
||||||
Update Date
|
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="w-full capitalize text-right truncate">
|
|
||||||
{getRelativeTime(row.original.updated_at)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
// TODO: deletion and editing for future sprint
|
|
||||||
// {
|
|
||||||
// id: "actions",
|
|
||||||
// enableHiding: false,
|
|
||||||
// cell: ({ row }) => {
|
|
||||||
// const checkpoint = row.original;
|
|
||||||
//
|
|
||||||
// return (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button variant="ghost" className="h-8 w-8 p-0">
|
|
||||||
// <span className="sr-only">Open menu</span>
|
|
||||||
// <MoreHorizontal className="h-4 w-4" />
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent align="end">
|
|
||||||
// <DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// className="text-destructive"
|
|
||||||
// onClick={() => {
|
|
||||||
// deleteWorkflow(checkpoint.id);
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// Delete Workflow
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function ModelList({ data }: { data: ModelItemList[] }) {
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const [columnVisibility, setColumnVisibility] = React.useState<
|
|
||||||
VisibilityState
|
|
||||||
>({});
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid grid-rows-[auto,1fr,auto] h-full">
|
|
||||||
<div className="flex flex-row w-full items-center py-4">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter workflows..."
|
|
||||||
value={(table.getColumn("model_name")?.getFilterValue() as string) ??
|
|
||||||
""}
|
|
||||||
onChange={(event) =>
|
|
||||||
table.getColumn("model_name")?.setFilterValue(event.target.value)}
|
|
||||||
className="max-w-sm"
|
|
||||||
/>
|
|
||||||
<div className="ml-auto flex gap-2">
|
|
||||||
<InsertModal
|
|
||||||
dialogClassName="sm:max-w-[600px]"
|
|
||||||
disabled={
|
|
||||||
false // TODO: limitations based on plan
|
|
||||||
}
|
|
||||||
title="Add a Model"
|
|
||||||
description="using a link to a model"
|
|
||||||
serverAction={addModel}
|
|
||||||
formSchema={downloadUrlModelSchema}
|
|
||||||
fieldConfig={{
|
|
||||||
url: {
|
|
||||||
fieldType: "fallback",
|
|
||||||
inputProps: { required: true },
|
|
||||||
description: (
|
|
||||||
<>
|
|
||||||
Pick a model from{" "}
|
|
||||||
<a
|
|
||||||
href="https://www.civitai.com/models"
|
|
||||||
target="_blank"
|
|
||||||
className="underline text-blue-600 hover:text-blue-800 visited:text-purple-600"
|
|
||||||
>
|
|
||||||
civitai.com
|
|
||||||
</a>{" "}
|
|
||||||
or a url we can download a model from
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
model_type: {
|
|
||||||
fieldType: "select",
|
|
||||||
inputProps: { required: true },
|
|
||||||
description: (
|
|
||||||
<>
|
|
||||||
We'll figure this out if you pick a civitai model
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ScrollArea className="h-full w-full rounded-md border">
|
|
||||||
<Table>
|
|
||||||
<TableHeader className="bg-background top-0 sticky">
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id}>
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{header.isPlaceholder ? null : flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext(),
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{table.getRowModel().rows?.length
|
|
||||||
? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(
|
|
||||||
cell.column.columnDef.cell,
|
|
||||||
cell.getContext(),
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell
|
|
||||||
colSpan={columns.length}
|
|
||||||
className="h-24 text-center"
|
|
||||||
>
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</ScrollArea>
|
|
||||||
<div className="flex flex-row items-center justify-end space-x-2 py-4">
|
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<div className="space-x-2">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => table.previousPage()}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
>
|
|
||||||
Previous
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => table.nextPage()}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
>
|
|
||||||
Next
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
} from "@clerk/nextjs";
|
} from "@clerk/nextjs";
|
||||||
import { Github, Menu } from "lucide-react";
|
import { Github, Menu } from "lucide-react";
|
||||||
import meta from "next-gen/config";
|
import meta from "next-gen/config";
|
||||||
import { useFeatureFlagEnabled } from "posthog-js/react";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useMediaQuery } from "usehooks-ts";
|
import { useMediaQuery } from "usehooks-ts";
|
||||||
|
|
||||||
@@ -30,17 +29,15 @@ export function Navbar() {
|
|||||||
const { organization } = useOrganization();
|
const { organization } = useOrganization();
|
||||||
const _isDesktop = useMediaQuery("(min-width: 1024px)");
|
const _isDesktop = useMediaQuery("(min-width: 1024px)");
|
||||||
const [isDesktop, setIsDesktop] = useState(true);
|
const [isDesktop, setIsDesktop] = useState(true);
|
||||||
|
const [isSheetOpen, setSheetOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsDesktop(_isDesktop);
|
setIsDesktop(_isDesktop);
|
||||||
}, [_isDesktop]);
|
}, [_isDesktop]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row items-center gap-4">
|
<div className="flex flex-row items-center gap-4">
|
||||||
{!isDesktop && (
|
{!isDesktop && (
|
||||||
<Sheet>
|
<Sheet open={isSheetOpen} onOpenChange={(open) => setSheetOpen(open)}>
|
||||||
<SheetTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<button className="flex items-center justify-center w-8 h-8 p-2">
|
<button className="flex items-center justify-center w-8 h-8 p-2">
|
||||||
<Menu />
|
<Menu />
|
||||||
@@ -51,7 +48,17 @@ export function Navbar() {
|
|||||||
<SheetTitle className="text-start">Comfy Deploy</SheetTitle>
|
<SheetTitle className="text-start">Comfy Deploy</SheetTitle>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
<div className="grid h-full grid-rows-[1fr_auto]">
|
<div className="grid h-full grid-rows-[1fr_auto]">
|
||||||
<NavbarMenu className=" h-full" />
|
<NavbarMenu
|
||||||
|
className=" h-full"
|
||||||
|
closeSheet={() => setSheetOpen(false)}
|
||||||
|
/>
|
||||||
|
{/* <OrganizationSwitcher
|
||||||
|
appearance={{
|
||||||
|
elements: {
|
||||||
|
rootBox: "flex items-center justify-center z-[50]",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/> */}
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button variant="outline">
|
<Button variant="outline">
|
||||||
@@ -82,26 +89,20 @@ export function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
{isDesktop && <NavbarMenu />}
|
{isDesktop && <NavbarMenu />}
|
||||||
{/* <Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
variant="link"
|
variant="link"
|
||||||
className="rounded-full aspect-square p-2 mr-4"
|
className="rounded-full aspect-square p-2 mr-4"
|
||||||
>
|
>
|
||||||
<a href="/docs">Docs</a>
|
<a href="/docs">Docs</a>
|
||||||
</Button> */}
|
</Button>
|
||||||
<UserButton
|
<UserButton />
|
||||||
afterSignOutUrl="/"
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="rounded-full aspect-square p-2"
|
className="rounded-full aspect-square p-2"
|
||||||
>
|
>
|
||||||
<a
|
<a target="_blank" href="https://github.com/BennyKok/comfyui-deploy" rel="noreferrer">
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/BennyKok/comfyui-deploy"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
<Github />
|
<Github />
|
||||||
</a>
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,11 +6,16 @@ import { cn } from "@/lib/utils";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useFeatureFlagEnabled } from "posthog-js/react";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useMediaQuery } from "usehooks-ts";
|
import { useMediaQuery } from "usehooks-ts";
|
||||||
|
|
||||||
export function NavbarMenu({ className }: { className?: string }) {
|
export function NavbarMenu({
|
||||||
|
className,
|
||||||
|
closeSheet,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
closeSheet?: () => void;
|
||||||
|
}) {
|
||||||
const _isDesktop = useMediaQuery("(min-width: 1024px)");
|
const _isDesktop = useMediaQuery("(min-width: 1024px)");
|
||||||
const [isDesktop, setIsDesktop] = useState(true);
|
const [isDesktop, setIsDesktop] = useState(true);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -22,8 +27,6 @@ export function NavbarMenu({ className }: { className?: string }) {
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const pricingPlanFlagEnable = useFeatureFlagEnabled("pricing-plan");
|
|
||||||
|
|
||||||
const pages = [
|
const pages = [
|
||||||
{
|
{
|
||||||
name: "Workflows",
|
name: "Workflows",
|
||||||
@@ -38,27 +41,11 @@ export function NavbarMenu({ className }: { className?: string }) {
|
|||||||
path: "/api-keys",
|
path: "/api-keys",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Storage",
|
name: "Examples",
|
||||||
path: "/storage",
|
path: "/examples"
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (pricingPlanFlagEnable) {
|
|
||||||
pages.push({
|
|
||||||
name: "Pricing",
|
|
||||||
path: "/pricing",
|
|
||||||
})
|
|
||||||
pages.push({
|
|
||||||
name: "Usage",
|
|
||||||
path: "/usage",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pages.push({
|
|
||||||
name: "Docs",
|
|
||||||
path: "/docs",
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("mr-2", className)}>
|
<div className={cn("mr-2", className)}>
|
||||||
{/* <div className="w-full h-full absolute inset-x-0 top-0 flex items-center justify-center pointer-events-none"> */}
|
{/* <div className="w-full h-full absolute inset-x-0 top-0 flex items-center justify-center pointer-events-none"> */}
|
||||||
@@ -67,7 +54,7 @@ export function NavbarMenu({ className }: { className?: string }) {
|
|||||||
defaultValue={pathname}
|
defaultValue={pathname}
|
||||||
className="w-fit flex pointer-events-auto"
|
className="w-fit flex pointer-events-auto"
|
||||||
>
|
>
|
||||||
<TabsList className=" w-full ">
|
<TabsList className="w-full">
|
||||||
{pages.map((page) => (
|
{pages.map((page) => (
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
key={page.name}
|
key={page.name}
|
||||||
@@ -91,6 +78,9 @@ export function NavbarMenu({ className }: { className?: string }) {
|
|||||||
<Link
|
<Link
|
||||||
key={page.name}
|
key={page.name}
|
||||||
href={page.path}
|
href={page.path}
|
||||||
|
onClick={() => {
|
||||||
|
if (!!closeSheet) closeSheet();
|
||||||
|
}}
|
||||||
className="p-2 hover:bg-gray-100/20 hover:underline"
|
className="p-2 hover:bg-gray-100/20 hover:underline"
|
||||||
>
|
>
|
||||||
{page.name}
|
{page.name}
|
||||||
|
|||||||
@@ -5,6 +5,20 @@ export async function OutputRender(props: {
|
|||||||
run_id: string;
|
run_id: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
}) {
|
}) {
|
||||||
|
if (props.filename.endsWith(".mp4") || props.filename.endsWith(".webm")) {
|
||||||
|
const url = await getFileDownloadUrl(
|
||||||
|
`outputs/runs/${props.run_id}/${props.filename}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<video controls autoPlay className="w-[400px]">
|
||||||
|
<source src={url} type="video/mp4" />
|
||||||
|
<source src={url} type="video/webm" />
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
props.filename.endsWith(".png") ||
|
props.filename.endsWith(".png") ||
|
||||||
props.filename.endsWith(".gif") ||
|
props.filename.endsWith(".gif") ||
|
||||||
@@ -12,13 +26,13 @@ export async function OutputRender(props: {
|
|||||||
props.filename.endsWith(".jpeg")
|
props.filename.endsWith(".jpeg")
|
||||||
) {
|
) {
|
||||||
const url = await getFileDownloadUrl(
|
const url = await getFileDownloadUrl(
|
||||||
`outputs/runs/${props.run_id}/${props.filename}`
|
`outputs/runs/${props.run_id}/${props.filename}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return <img className="max-w-[200px]" alt={props.filename} src={url} />;
|
return <img className="max-w-[200px]" alt={props.filename} src={url} />;
|
||||||
} else {
|
} else {
|
||||||
const url = await getFileDownloadUrl(
|
const url = await getFileDownloadUrl(
|
||||||
`outputs/runs/${props.run_id}/${props.filename}`
|
`outputs/runs/${props.run_id}/${props.filename}`,
|
||||||
);
|
);
|
||||||
// console.log(url);
|
// console.log(url);
|
||||||
|
|
||||||
|
|||||||
@@ -1,411 +0,0 @@
|
|||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Check, Info, Minus } from "lucide-react";
|
|
||||||
|
|
||||||
import { Fragment } from "react";
|
|
||||||
import { auth } from "@clerk/nextjs";
|
|
||||||
import { getCurrentPlan } from "../server/getCurrentPlan";
|
|
||||||
|
|
||||||
const tiers = [
|
|
||||||
{
|
|
||||||
name: "Basic",
|
|
||||||
id: "basic",
|
|
||||||
// href: "/api/checkout?plan=basic",
|
|
||||||
href: "/workflows",
|
|
||||||
priceMonthly: "$0",
|
|
||||||
description: "Instant Comfy UI API",
|
|
||||||
mostPopular: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Pro",
|
|
||||||
id: "pro",
|
|
||||||
href: "/api/stripe/checkout?plan=pro",
|
|
||||||
priceMonthly: "$20",
|
|
||||||
description: "Accelerate Comfy UI",
|
|
||||||
mostPopular: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Enterprise",
|
|
||||||
id: "enterprise",
|
|
||||||
href: "/api/stripe/checkout?plan=enterprise",
|
|
||||||
priceMonthly: "$100",
|
|
||||||
description: "Scale your Products",
|
|
||||||
mostPopular: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
type TierFeature = {
|
|
||||||
Basic?: string | JSX.Element | boolean;
|
|
||||||
Pro?: string | JSX.Element | boolean;
|
|
||||||
Enterprise?: string | JSX.Element | boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Feature = {
|
|
||||||
name: string;
|
|
||||||
tiers: TierFeature;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Section = {
|
|
||||||
name: string;
|
|
||||||
features: Feature[];
|
|
||||||
};
|
|
||||||
const sections: Section[] = [
|
|
||||||
{
|
|
||||||
name: "Features",
|
|
||||||
features: [
|
|
||||||
{
|
|
||||||
name: "GPU",
|
|
||||||
tiers: { Basic: "T4", Pro: "T4, A10G", Enterprise: "T4, A10G, A100" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Compute Credit",
|
|
||||||
tiers: {
|
|
||||||
Basic: (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger className="flex items-center justify-center gap-2">
|
|
||||||
30k secs free + usage <Info size={14} />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<ul className="flex flex-col items-start justify-start">
|
|
||||||
GPU Price /s = $0.00015
|
|
||||||
<li>- T4 Multiplier = x1</li>
|
|
||||||
</ul>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
Pro: (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger className="flex items-center justify-center gap-2">
|
|
||||||
30k secs free + usage <Info size={14} />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<ul className="flex flex-col items-start justify-start">
|
|
||||||
GPU Price /s = $0.00015
|
|
||||||
<li>- T4 Multiplier = x1</li>
|
|
||||||
<li>- A10G Multiplier = x4</li>
|
|
||||||
</ul>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
Enterprise: (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger className="flex items-center justify-center gap-2">
|
|
||||||
30k secs free + usage <Info size={14} />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<ul className="flex flex-col items-start justify-start">
|
|
||||||
GPU Price /s = $0.00015
|
|
||||||
<li>- T4 Multiplier = x1</li>
|
|
||||||
<li>- A10G Multiplier = x4</li>
|
|
||||||
<li>- A100 Multiplier = x7</li>
|
|
||||||
</ul>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Workflows",
|
|
||||||
tiers: { Basic: "2", Pro: "25", Enterprise: "Unlimited" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Serverless Machines",
|
|
||||||
tiers: { Basic: "2", Pro: "10", Enterprise: "Unlimited" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Outputs Storage",
|
|
||||||
tiers: { Basic: "2 GB", Pro: "10 GB", Enterprise: "Unlimited" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Dedicated Support",
|
|
||||||
tiers: { Enterprise: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Private Model Hosting",
|
|
||||||
tiers: { Enterprise: "Coming Soon" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default async function PricingList() {
|
|
||||||
const { userId, orgId } = auth();
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
return <>No user id</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sub = await getCurrentPlan({ user_id: userId, org_id: orgId });
|
|
||||||
|
|
||||||
const getHrefFromTier = (tier: (typeof tiers)[0]) => {
|
|
||||||
if (sub?.status == "active") {
|
|
||||||
if (tier.id == sub?.plan) return "/api/stripe/dashboard";
|
|
||||||
// This is actually cancelled
|
|
||||||
if (sub.cancel_at_period_end) return tier.href;
|
|
||||||
return "/api/stripe/dashboard?change=true";
|
|
||||||
} else {
|
|
||||||
return tier.href;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const getNameFromTier = (tier: (typeof tiers)[0]) => {
|
|
||||||
if (tier.id == sub?.plan && sub.status == "active") {
|
|
||||||
return sub.cancel_at_period_end ? (
|
|
||||||
<>
|
|
||||||
Current <span className="text-2xs"> - Ending this period</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
"Current"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (sub?.status == "active") {
|
|
||||||
return "Get Started";
|
|
||||||
} else {
|
|
||||||
return "Get Started";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-white py-24 sm:py-32">
|
|
||||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
|
||||||
<div className="mx-auto max-w-4xl text-center">
|
|
||||||
<h2 className="text-base font-semibold leading-7 text-indigo-600">
|
|
||||||
Pricing
|
|
||||||
</h2>
|
|
||||||
<p className="mt-2 text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
|
|
||||||
Turn any workflow into API
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p className="mx-auto mt-6 max-w-2xl text-center text-lg leading-8 text-gray-600">
|
|
||||||
ComfyDeploy is now under technical preview.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* xs to lg */}
|
|
||||||
<div className="mx-auto mt-12 max-w-md space-y-8 sm:mt-16 lg:hidden">
|
|
||||||
{tiers.map((tier) => (
|
|
||||||
<section
|
|
||||||
key={tier.id}
|
|
||||||
className={cn(
|
|
||||||
tier.mostPopular
|
|
||||||
? "rounded-xl bg-gray-400/5 ring-1 ring-inset ring-gray-200"
|
|
||||||
: "",
|
|
||||||
"p-8",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<h3
|
|
||||||
id={tier.id}
|
|
||||||
className="text-sm font-semibold leading-6 text-gray-900"
|
|
||||||
>
|
|
||||||
{tier.name}
|
|
||||||
</h3>
|
|
||||||
<p className="mt-2 flex items-baseline gap-x-1 text-gray-900">
|
|
||||||
<span className="text-4xl font-bold">{tier.priceMonthly}</span>
|
|
||||||
<span className="text-sm font-semibold">/month</span>
|
|
||||||
</p>
|
|
||||||
<br></br>
|
|
||||||
<div className="text-xl font-semibold">{tier.description}</div>
|
|
||||||
<a
|
|
||||||
href={getHrefFromTier(tier)}
|
|
||||||
aria-describedby={tier.id}
|
|
||||||
className={cn(
|
|
||||||
tier.mostPopular
|
|
||||||
? "bg-indigo-600 text-white hover:bg-indigo-500"
|
|
||||||
: "text-indigo-600 ring-1 ring-inset ring-indigo-200 hover:ring-indigo-300",
|
|
||||||
"mt-8 block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{getNameFromTier(tier)}
|
|
||||||
</a>
|
|
||||||
<ul
|
|
||||||
role="list"
|
|
||||||
className="mt-10 space-y-4 text-sm leading-6 text-gray-900"
|
|
||||||
>
|
|
||||||
{sections.map((section) => (
|
|
||||||
<li key={section.name}>
|
|
||||||
<ul role="list" className="space-y-4">
|
|
||||||
{section.features.map((feature) =>
|
|
||||||
feature.tiers[tier.name as keyof TierFeature] ? (
|
|
||||||
<li key={feature.name} className="flex gap-x-3">
|
|
||||||
<Check
|
|
||||||
className="h-6 w-5 flex-none text-indigo-600"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
{feature.name}{" "}
|
|
||||||
{typeof feature.tiers[
|
|
||||||
tier.name as keyof TierFeature
|
|
||||||
] === "string" ? (
|
|
||||||
<span className="text-sm leading-6 text-gray-500">
|
|
||||||
(
|
|
||||||
{
|
|
||||||
feature.tiers[
|
|
||||||
tier.name as keyof TierFeature
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
) : null,
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* lg+ */}
|
|
||||||
<div className="isolate mt-20 hidden lg:block border-gray-100 border p-6 shadow-md rounded-lg">
|
|
||||||
<div className="relative -mx-8">
|
|
||||||
{tiers.some((tier) => tier.mostPopular) ? (
|
|
||||||
<div className="absolute inset-x-4 inset-y-0 -z-10 flex">
|
|
||||||
<div
|
|
||||||
className="flex w-1/4 px-4"
|
|
||||||
aria-hidden="true"
|
|
||||||
style={{
|
|
||||||
marginLeft: `${
|
|
||||||
(tiers.findIndex((tier) => tier.mostPopular) + 1) * 25
|
|
||||||
}%`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="w-full rounded-t-xl border-x border-t border-gray-900/10 bg-gray-400/5" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<table className="w-full table-fixed border-separate border-spacing-x-8 text-left">
|
|
||||||
<caption className="sr-only">Pricing plan comparison</caption>
|
|
||||||
<colgroup>
|
|
||||||
<col className="w-1/4" />
|
|
||||||
<col className="w-1/4" />
|
|
||||||
<col className="w-1/4" />
|
|
||||||
<col className="w-1/4" />
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td />
|
|
||||||
{tiers.map((tier) => (
|
|
||||||
<th
|
|
||||||
key={tier.id}
|
|
||||||
scope="col"
|
|
||||||
className="px-6 pt-6 xl:px-8 xl:pt-8"
|
|
||||||
>
|
|
||||||
<div className="text-sm font-semibold leading-7 text-gray-900">
|
|
||||||
{tier.name}
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">
|
|
||||||
<span className="sr-only">Price</span>
|
|
||||||
</th>
|
|
||||||
{tiers.map((tier) => (
|
|
||||||
<td key={tier.id} className="px-6 pt-2 xl:px-8">
|
|
||||||
<div className="flex items-baseline gap-x-1 text-gray-900">
|
|
||||||
<span className="text-4xl font-bold">
|
|
||||||
{tier.priceMonthly}
|
|
||||||
</span>
|
|
||||||
<span className="text-sm font-semibold leading-6">
|
|
||||||
/month
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<br></br>
|
|
||||||
<div className="text-md font-semibold">
|
|
||||||
{tier.description}
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href={getHrefFromTier(tier)}
|
|
||||||
className={cn(
|
|
||||||
tier.mostPopular
|
|
||||||
? "bg-indigo-600 text-white hover:bg-indigo-500"
|
|
||||||
: "text-indigo-600 ring-1 ring-inset ring-indigo-200 hover:ring-indigo-300",
|
|
||||||
"mt-8 block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{getNameFromTier(tier)}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
{sections.map((section, sectionIdx) => (
|
|
||||||
<Fragment key={section.name}>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
scope="colgroup"
|
|
||||||
colSpan={4}
|
|
||||||
className={cn(
|
|
||||||
sectionIdx === 0 ? "pt-8" : "pt-16",
|
|
||||||
"pb-4 text-sm font-semibold leading-6 text-gray-900",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{section.name}
|
|
||||||
<div className="absolute inset-x-8 mt-4 h-px bg-gray-900/10" />
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
{section.features.map((feature) => (
|
|
||||||
<tr key={feature.name}>
|
|
||||||
<th
|
|
||||||
scope="row"
|
|
||||||
className="py-4 text-sm font-normal leading-6 text-gray-900"
|
|
||||||
>
|
|
||||||
{feature.name}
|
|
||||||
<div className="absolute inset-x-8 mt-4 h-px bg-gray-900/5" />
|
|
||||||
</th>
|
|
||||||
{tiers.map((tier) => (
|
|
||||||
<td key={tier.id} className="px-6 py-4 xl:px-8">
|
|
||||||
{typeof feature.tiers[
|
|
||||||
tier.name as keyof TierFeature
|
|
||||||
] === "string" ||
|
|
||||||
typeof feature.tiers[
|
|
||||||
tier.name as keyof TierFeature
|
|
||||||
] === "object" ? (
|
|
||||||
<div className="flex items-center justify-center text-center text-sm leading-6 text-gray-500">
|
|
||||||
{feature.tiers[tier.name as keyof TierFeature]}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{feature.tiers[
|
|
||||||
tier.name as keyof TierFeature
|
|
||||||
] === true ? (
|
|
||||||
<Check
|
|
||||||
className="mx-auto h-5 w-5 text-indigo-600"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Minus
|
|
||||||
className="mx-auto h-5 w-5 text-gray-400"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<span className="sr-only">
|
|
||||||
{feature.tiers[
|
|
||||||
tier.name as keyof TierFeature
|
|
||||||
] === true
|
|
||||||
? "Included"
|
|
||||||
: "Not included"}{" "}
|
|
||||||
in {tier.name}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -2,18 +2,18 @@ import { RunInputs } from "@/components/RunInputs";
|
|||||||
import { RunOutputs } from "@/components/RunOutputs";
|
import { RunOutputs } from "@/components/RunOutputs";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { TableCell, TableRow } from "@/components/ui/table";
|
import { TableCell, TableRow } from "@/components/ui/table";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { getDuration, getRelativeTime } from "@/lib/getRelativeTime";
|
import { getDuration, getRelativeTime } from "@/lib/getRelativeTime";
|
||||||
import { type findAllRuns } from "@/server/findAllRuns";
|
import { type findAllRuns } from "@/server/findAllRuns";
|
||||||
@@ -21,64 +21,54 @@ import { Suspense } from "react";
|
|||||||
import { LiveStatus } from "./LiveStatus";
|
import { LiveStatus } from "./LiveStatus";
|
||||||
|
|
||||||
export async function RunDisplay({
|
export async function RunDisplay({
|
||||||
run,
|
run,
|
||||||
}: {
|
}: {
|
||||||
run: Awaited<ReturnType<typeof findAllRuns>>[0];
|
run: Awaited<ReturnType<typeof findAllRuns>>[0];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>
|
<TableCell>{run.number}</TableCell>
|
||||||
<Tooltip>
|
<TableCell className="font-medium truncate">
|
||||||
<TooltipTrigger>{run.number}</TooltipTrigger>
|
{run.machine?.name}
|
||||||
<TooltipContent>{run.id}</TooltipContent>
|
</TableCell>
|
||||||
</Tooltip>
|
<TableCell className="truncate">
|
||||||
</TableCell>
|
{getRelativeTime(run.created_at)}
|
||||||
<TableCell className="font-medium truncate">
|
</TableCell>
|
||||||
{run.machine?.name}
|
<TableCell>{run.version?.version}</TableCell>
|
||||||
</TableCell>
|
<TableCell>
|
||||||
<TableCell className="truncate">
|
<Badge variant="outline" className="truncate">
|
||||||
{getRelativeTime(run.created_at)}
|
{run.origin}
|
||||||
</TableCell>
|
</Badge>
|
||||||
<TableCell>{run.version?.version}</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell className="truncate">
|
||||||
<Badge variant="outline" className="truncate">
|
<Tooltip>
|
||||||
{run.origin}
|
<TooltipTrigger>{getDuration(run.duration)}</TooltipTrigger>
|
||||||
</Badge>
|
<TooltipContent>
|
||||||
</TableCell>
|
<div>Cold start: {getDuration(run.cold_start_duration)}</div>
|
||||||
<TableCell className="truncate">
|
<div>Run duration: {getDuration(run.run_duration)}</div>
|
||||||
<Tooltip>
|
</TooltipContent>
|
||||||
<TooltipTrigger>{getDuration(run.duration)}</TooltipTrigger>
|
</Tooltip>
|
||||||
<TooltipContent>
|
</TableCell>
|
||||||
<div>
|
<LiveStatus run={run} />
|
||||||
Serverless latency: {getDuration(run.comfy_deploy_cold_start)}
|
</TableRow>
|
||||||
</div>
|
</DialogTrigger>
|
||||||
<div>
|
<DialogContent className="max-w-3xl">
|
||||||
GPU Cold start: {getDuration(run.cold_start_duration)}
|
<DialogHeader>
|
||||||
</div>
|
<DialogTitle>Run outputs</DialogTitle>
|
||||||
<div>Run duration: {getDuration(run.run_duration)}</div>
|
<DialogDescription>
|
||||||
</TooltipContent>
|
You can view your run's outputs here
|
||||||
</Tooltip>
|
</DialogDescription>
|
||||||
</TableCell>
|
</DialogHeader>
|
||||||
<LiveStatus run={run} />
|
<div className="max-h-96 overflow-y-scroll">
|
||||||
</TableRow>
|
<RunInputs run={run} />
|
||||||
</DialogTrigger>
|
<Suspense>
|
||||||
<DialogContent className="max-w-3xl">
|
<RunOutputs run_id={run.id} />
|
||||||
<DialogHeader>
|
</Suspense>
|
||||||
<DialogTitle>Run outputs</DialogTitle>
|
</div>
|
||||||
<DialogDescription>
|
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}
|
||||||
You can view your run's outputs here
|
</DialogContent>
|
||||||
</DialogDescription>
|
</Dialog>
|
||||||
</DialogHeader>
|
);
|
||||||
<div className="max-h-96 overflow-y-scroll">
|
|
||||||
<RunInputs run={run} />
|
|
||||||
<Suspense>
|
|
||||||
<RunOutputs run_id={run.id} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import { callServerPromise } from "./callServerPromise";
|
|||||||
import fetcher from "./fetcher";
|
import fetcher from "./fetcher";
|
||||||
import { ButtonAction } from "@/components/ButtonActionLoader";
|
import { ButtonAction } from "@/components/ButtonActionLoader";
|
||||||
import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
|
import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
|
||||||
|
import { VisualizeImagesGrid } from "@/components/VisualizeImagesGrid";
|
||||||
|
|
||||||
export function VersionSelect({
|
export function VersionSelect({
|
||||||
workflow,
|
workflow,
|
||||||
@@ -124,30 +125,66 @@ export function MachineSelect({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SelectedMachineStore = {
|
||||||
|
selectedMachine: string | undefined;
|
||||||
|
setSelectedMachine: (machine: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectedMachineStore = create<SelectedMachineStore>((set) => ({
|
||||||
|
selectedMachine: undefined,
|
||||||
|
setSelectedMachine: (machine) => set(() => ({ selectedMachine: machine })),
|
||||||
|
}));
|
||||||
|
|
||||||
export function useSelectedMachine(
|
export function useSelectedMachine(
|
||||||
machines: Awaited<ReturnType<typeof getMachines>>,
|
machines: Awaited<ReturnType<typeof getMachines>>,
|
||||||
) {
|
): [string, (v: string) => void] {
|
||||||
const a = useQueryState("machine", {
|
const { selectedMachine, setSelectedMachine } = selectedMachineStore();
|
||||||
defaultValue: machines?.[0]?.id ?? "",
|
return [selectedMachine ?? machines?.[0]?.id ?? "", setSelectedMachine];
|
||||||
});
|
|
||||||
|
|
||||||
return a;
|
// const searchParams = useSearchParams();
|
||||||
|
// const pathname = usePathname();
|
||||||
|
// const router = useRouter();
|
||||||
|
|
||||||
|
// const createQueryString = useCallback(
|
||||||
|
// (name: string, value: string) => {
|
||||||
|
// const params = new URLSearchParams(searchParams.toString());
|
||||||
|
// params.set(name, value);
|
||||||
|
|
||||||
|
// return params.toString();
|
||||||
|
// },
|
||||||
|
// [searchParams],
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return [
|
||||||
|
// searchParams.get("machine") ?? machines?.[0]?.id ?? "",
|
||||||
|
// (v: string) => {
|
||||||
|
// // window.history.pushState(
|
||||||
|
// // "new url",
|
||||||
|
// // "",
|
||||||
|
// // pathname + "?" + createQueryString("machine", v),
|
||||||
|
// // );
|
||||||
|
// // router.push(pathname + "?" + createQueryString("machine", v));
|
||||||
|
// router.replace(pathname + "?" + createQueryString("machine", v));
|
||||||
|
// },
|
||||||
|
// ];
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublicRunStore = {
|
type PublicRunStore = {
|
||||||
image: string;
|
image: {
|
||||||
|
url: string;
|
||||||
|
}[] | null;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
runId: string;
|
runId: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
|
||||||
setImage: (image: string) => void;
|
setImage: (image: { url: string; }[]) => void;
|
||||||
setLoading: (loading: boolean) => void;
|
setLoading: (loading: boolean) => void;
|
||||||
setRunId: (runId: string) => void;
|
setRunId: (runId: string) => void;
|
||||||
setStatus: (status: string) => void;
|
setStatus: (status: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const publicRunStore = create<PublicRunStore>((set) => ({
|
export const publicRunStore = create<PublicRunStore>((set) => ({
|
||||||
image: "",
|
image: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
runId: "",
|
runId: "",
|
||||||
status: "",
|
status: "",
|
||||||
@@ -171,7 +208,10 @@ export function PublicRunOutputs(props: {
|
|||||||
console.log(res?.status);
|
console.log(res?.status);
|
||||||
if (res) setStatus(res.status);
|
if (res) setStatus(res.status);
|
||||||
if (res && res.status === "success") {
|
if (res && res.status === "success") {
|
||||||
setImage(res.outputs[0]?.data.images[0].url);
|
const imageURLs = res.outputs[0]?.data.images.map((item: { url: string; }) => {
|
||||||
|
return { url: item.url };
|
||||||
|
});
|
||||||
|
setImage(imageURLs);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
}
|
}
|
||||||
@@ -180,30 +220,25 @@ export function PublicRunOutputs(props: {
|
|||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [runId]);
|
}, [runId]);
|
||||||
|
|
||||||
return (
|
if (loading) {
|
||||||
<div className="border border-gray-200 w-full square h-[400px] rounded-lg relative">
|
return (
|
||||||
{!loading && !image && props.preview && props.preview.length > 0 && (
|
<div className="border border-gray-200 w-full h-[400px] square rounded-lg relative p-4 ">
|
||||||
<>
|
|
||||||
<img
|
|
||||||
className="w-full h-full object-contain"
|
|
||||||
src={props.preview[0]?.url}
|
|
||||||
alt="Generated image"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!loading && image && (
|
|
||||||
<img
|
|
||||||
className="w-full h-full object-contain"
|
|
||||||
src={image}
|
|
||||||
alt="Generated image"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{loading && (
|
|
||||||
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
|
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
|
||||||
{status} <LoadingIcon />
|
{status} <LoadingIcon />
|
||||||
</div>
|
</div>
|
||||||
|
<Skeleton className="w-full h-full" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="border border-gray-200 w-full min-h-[400px] square rounded-lg relative p-4 ">
|
||||||
|
{!image && props.preview && props.preview.length > 0 &&
|
||||||
|
<VisualizeImagesGrid images={props.preview} />
|
||||||
|
}
|
||||||
|
{image && (
|
||||||
|
<VisualizeImagesGrid images={image} />
|
||||||
)}
|
)}
|
||||||
{loading && <Skeleton className="w-full h-full" />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -294,7 +329,7 @@ export function RunWorkflowButton({
|
|||||||
className="px-1"
|
className="px-1"
|
||||||
>
|
>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<AutoFormSubmit>
|
<AutoFormSubmit disabled={isLoading}>
|
||||||
Run
|
Run
|
||||||
{isLoading ? <LoadingIcon /> : <Play size={14} />}
|
{isLoading ? <LoadingIcon /> : <Play size={14} />}
|
||||||
</AutoFormSubmit>
|
</AutoFormSubmit>
|
||||||
@@ -402,7 +437,7 @@ export function OpenEditButton({
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const url = await callServerPromise(
|
const url = await callServerPromise(
|
||||||
editWorkflowOnMachine(machine, workflow_version_id),
|
editWorkflowOnMachine(workflow_version_id, machine),
|
||||||
);
|
);
|
||||||
if (url && typeof url !== "object") {
|
if (url && typeof url !== "object") {
|
||||||
window.open(url, "_blank");
|
window.open(url, "_blank");
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
type imagesType = {
|
||||||
|
url: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
};
|
||||||
|
type VisualizeImagesGridProps = {
|
||||||
|
images: imagesType[];
|
||||||
|
layout?: 'justify-between' | 'justify-center' | 'justify-start' | 'justify-end';
|
||||||
|
};
|
||||||
|
export function VisualizeImagesGrid({ images, layout }: VisualizeImagesGridProps) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex gap-4 flex-wrap ${layout || 'justify-center'}`}>
|
||||||
|
<>
|
||||||
|
{images && images.length > 0 &&
|
||||||
|
images.map(item => {
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (item?.url.endsWith(".mp4") || item?.url.endsWith(".webm")) {
|
||||||
|
return (
|
||||||
|
<video key={item?.url} controls autoPlay className="rounded-xl" style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}>
|
||||||
|
<source src={item?.url} type="video/mp4" />
|
||||||
|
<source src={item?.url} type="video/webm" />
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <img
|
||||||
|
key={item?.url}
|
||||||
|
className="object-contain overflow-hidden rounded-xl"
|
||||||
|
src={item?.url}
|
||||||
|
alt="Generated image"
|
||||||
|
style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}
|
||||||
|
/>;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,13 +2,7 @@
|
|||||||
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export async function callServerPromise<T>(result: Promise<T>, props?: {
|
export async function callServerPromise<T>(result: Promise<T>) {
|
||||||
loadingText: string
|
|
||||||
}) {
|
|
||||||
let id: string | number
|
|
||||||
if (props?.loadingText) {
|
|
||||||
id = toast.loading(props.loadingText)
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
.then((x) => {
|
.then((x) => {
|
||||||
if ((x as { message: string })?.message !== undefined) {
|
if ((x as { message: string })?.message !== undefined) {
|
||||||
@@ -21,8 +15,5 @@ export async function callServerPromise<T>(result: Promise<T>, props?: {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
return null;
|
return null;
|
||||||
}).finally(() => {
|
|
||||||
if (id != undefined)
|
|
||||||
toast.dismiss(id)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,6 @@ type CustomNodeList = {
|
|||||||
author: string;
|
author: string;
|
||||||
title: string;
|
title: string;
|
||||||
reference: string;
|
reference: string;
|
||||||
pip: string[];
|
|
||||||
files: string[];
|
files: string[];
|
||||||
install_type: string;
|
install_type: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -238,12 +237,56 @@ function CustomNodesSelector({
|
|||||||
key={index}
|
key={index}
|
||||||
value={framework.reference}
|
value={framework.reference}
|
||||||
onSelect={async (currentValue) => {
|
onSelect={async (currentValue) => {
|
||||||
|
const repoName = extractRepoName(currentValue);
|
||||||
|
|
||||||
|
console.log(repoName);
|
||||||
|
|
||||||
|
const id = toast.loading(`Fetching repo info...`);
|
||||||
|
// toast.info("hi");
|
||||||
|
|
||||||
|
const repo = await fetch(
|
||||||
|
`https://api.github.com/repos/${repoName}`
|
||||||
|
)
|
||||||
|
.then((x) => x.json())
|
||||||
|
.then((x) => {
|
||||||
|
console.log(x);
|
||||||
|
return x;
|
||||||
|
})
|
||||||
|
.then((x) => RepoSchema.parse(x))
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
toast.dismiss(id);
|
||||||
|
toast.error(`Failed to fetch repo info ${e.message}`);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!repo) return;
|
||||||
|
|
||||||
|
const branch = repo.default_branch;
|
||||||
|
|
||||||
|
const branchInfo = await fetch(
|
||||||
|
`https://api.github.com/repos/${repoName}/branches/${branch}`
|
||||||
|
)
|
||||||
|
.then((x) => x.json())
|
||||||
|
.then((x) => BranchInfoSchema.parse(x))
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
toast.dismiss(id);
|
||||||
|
toast.error(
|
||||||
|
`Failed to fetch branch info ${e.message}`
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
toast.dismiss(id);
|
||||||
|
|
||||||
|
if (!branchInfo) return;
|
||||||
|
|
||||||
let nodeList: Record<
|
let nodeList: Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
hash: string;
|
hash: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
pip?: string[]
|
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
const x = customNodeList;
|
const x = customNodeList;
|
||||||
@@ -253,49 +296,10 @@ function CustomNodesSelector({
|
|||||||
delete newNodeList[currentValue];
|
delete newNodeList[currentValue];
|
||||||
nodeList = newNodeList;
|
nodeList = newNodeList;
|
||||||
} else {
|
} else {
|
||||||
const repoName = extractRepoName(currentValue);
|
|
||||||
const id = toast.loading(`Fetching repo info...`);
|
|
||||||
const repo = await fetch(
|
|
||||||
`https://api.github.com/repos/${repoName}`
|
|
||||||
)
|
|
||||||
.then((x) => x.json())
|
|
||||||
.then((x) => {
|
|
||||||
console.log(x);
|
|
||||||
return x;
|
|
||||||
})
|
|
||||||
.then((x) => RepoSchema.parse(x))
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
toast.dismiss(id);
|
|
||||||
toast.error(`Failed to fetch repo info ${e.message}`);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!repo) return;
|
|
||||||
const branch = repo.default_branch;
|
|
||||||
const branchInfo = await fetch(
|
|
||||||
`https://api.github.com/repos/${repoName}/branches/${branch}`
|
|
||||||
)
|
|
||||||
.then((x) => x.json())
|
|
||||||
.then((x) => BranchInfoSchema.parse(x))
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
toast.dismiss(id);
|
|
||||||
toast.error(
|
|
||||||
`Failed to fetch branch info ${e.message}`
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
toast.dismiss(id);
|
|
||||||
|
|
||||||
if (!branchInfo) return;
|
|
||||||
|
|
||||||
nodeList = {
|
nodeList = {
|
||||||
[currentValue]: {
|
[currentValue]: {
|
||||||
hash: branchInfo?.commit.sha,
|
hash: branchInfo?.commit.sha,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
pip: framework.pip
|
|
||||||
},
|
},
|
||||||
...x,
|
...x,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
|
||||||
|
|
||||||
const AlertDialog = AlertDialogPrimitive.Root
|
|
||||||
|
|
||||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
|
||||||
|
|
||||||
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
|
||||||
|
|
||||||
const AlertDialogOverlay = React.forwardRef<
|
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
|
||||||
>(({ className, ...props }, ref) => (
|
|
||||||
<AlertDialogPrimitive.Overlay
|
|
||||||
className={cn(
|
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
ref={ref}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
|
||||||
|
|
||||||
const AlertDialogContent = React.forwardRef<
|
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
|
||||||
>(({ className, ...props }, ref) => (
|
|
||||||
<AlertDialogPortal>
|
|
||||||
<AlertDialogOverlay />
|
|
||||||
<AlertDialogPrimitive.Content
|
|
||||||
ref={ref}
|
|
||||||
className={cn(
|
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</AlertDialogPortal>
|
|
||||||
))
|
|
||||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
|
||||||
|
|
||||||
const AlertDialogHeader = ({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"flex flex-col space-y-2 text-center sm:text-left",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
AlertDialogHeader.displayName = "AlertDialogHeader"
|
|
||||||
|
|
||||||
const AlertDialogFooter = ({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
AlertDialogFooter.displayName = "AlertDialogFooter"
|
|
||||||
|
|
||||||
const AlertDialogTitle = React.forwardRef<
|
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
|
||||||
>(({ className, ...props }, ref) => (
|
|
||||||
<AlertDialogPrimitive.Title
|
|
||||||
ref={ref}
|
|
||||||
className={cn("text-lg font-semibold", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
|
||||||
|
|
||||||
const AlertDialogDescription = React.forwardRef<
|
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
|
||||||
>(({ className, ...props }, ref) => (
|
|
||||||
<AlertDialogPrimitive.Description
|
|
||||||
ref={ref}
|
|
||||||
className={cn("text-sm text-muted-foreground", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
AlertDialogDescription.displayName =
|
|
||||||
AlertDialogPrimitive.Description.displayName
|
|
||||||
|
|
||||||
const AlertDialogAction = React.forwardRef<
|
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
|
||||||
>(({ className, ...props }, ref) => (
|
|
||||||
<AlertDialogPrimitive.Action
|
|
||||||
ref={ref}
|
|
||||||
className={cn(buttonVariants(), className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
|
||||||
|
|
||||||
const AlertDialogCancel = React.forwardRef<
|
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
|
||||||
>(({ className, ...props }, ref) => (
|
|
||||||
<AlertDialogPrimitive.Cancel
|
|
||||||
ref={ref}
|
|
||||||
className={cn(
|
|
||||||
buttonVariants({ variant: "outline" }),
|
|
||||||
"mt-2 sm:mt-0",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
|
||||||
|
|
||||||
export {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogPortal,
|
|
||||||
AlertDialogOverlay,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
const Progress = React.forwardRef<
|
|
||||||
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
|
||||||
>(({ className, value, ...props }, ref) => (
|
|
||||||
<ProgressPrimitive.Root
|
|
||||||
ref={ref}
|
|
||||||
className={cn(
|
|
||||||
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<ProgressPrimitive.Indicator
|
|
||||||
className="h-full w-full flex-1 bg-primary transition-all"
|
|
||||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
||||||
/>
|
|
||||||
</ProgressPrimitive.Root>
|
|
||||||
))
|
|
||||||
Progress.displayName = ProgressPrimitive.Root.displayName
|
|
||||||
|
|
||||||
export { Progress }
|
|
||||||
@@ -5,7 +5,7 @@ import * as schema from "./schema";
|
|||||||
const isDevContainer = process.env.REMOTE_CONTAINERS !== undefined;
|
const isDevContainer = process.env.REMOTE_CONTAINERS !== undefined;
|
||||||
|
|
||||||
// if we're running locally
|
// if we're running locally
|
||||||
if (process.env.VERCEL_ENV !== "production" && process.env.VERCEL_ENV !== "preview") {
|
if (process.env.VERCEL_ENV !== "production") {
|
||||||
// Set the WebSocket proxy to work with the local instance
|
// Set the WebSocket proxy to work with the local instance
|
||||||
if (isDevContainer) {
|
if (isDevContainer) {
|
||||||
// Running inside a VS Code devcontainer
|
// Running inside a VS Code devcontainer
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { CivitaiModelResponse } from "@/types/civitai";
|
|
||||||
import { type InferSelectModel, relations } from "drizzle-orm";
|
import { type InferSelectModel, relations } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
@@ -9,10 +8,9 @@ import {
|
|||||||
text,
|
text,
|
||||||
timestamp,
|
timestamp,
|
||||||
uuid,
|
uuid,
|
||||||
real,
|
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
import { TypeOf, z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const dbSchema = pgSchema("comfyui_deploy");
|
export const dbSchema = pgSchema("comfyui_deploy");
|
||||||
|
|
||||||
@@ -83,7 +81,6 @@ export const workflowVersionTable = dbSchema.table("workflow_versions", {
|
|||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const workflowVersionSchema = createSelectSchema(workflowVersionTable);
|
export const workflowVersionSchema = createSelectSchema(workflowVersionTable);
|
||||||
|
|
||||||
export const workflowVersionRelations = relations(
|
export const workflowVersionRelations = relations(
|
||||||
@@ -102,8 +99,6 @@ export const workflowRunStatus = pgEnum("workflow_run_status", [
|
|||||||
"uploading",
|
"uploading",
|
||||||
"success",
|
"success",
|
||||||
"failed",
|
"failed",
|
||||||
"started",
|
|
||||||
"queued",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const deploymentEnvironment = pgEnum("deployment_environment", [
|
export const deploymentEnvironment = pgEnum("deployment_environment", [
|
||||||
@@ -118,8 +113,6 @@ export const workflowRunOrigin = pgEnum("workflow_run_origin", [
|
|||||||
"public-share",
|
"public-share",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const WorkflowRunStatusSchema = z.enum(workflowRunStatus.enumValues);
|
|
||||||
|
|
||||||
export const WorkflowRunOriginSchema = z.enum(workflowRunOrigin.enumValues);
|
export const WorkflowRunOriginSchema = z.enum(workflowRunOrigin.enumValues);
|
||||||
export type WorkflowRunOriginType = z.infer<typeof WorkflowRunOriginSchema>;
|
export type WorkflowRunOriginType = z.infer<typeof WorkflowRunOriginSchema>;
|
||||||
|
|
||||||
@@ -162,16 +155,8 @@ export const workflowRunsTable = dbSchema.table("workflow_runs", {
|
|||||||
origin: workflowRunOrigin("origin").notNull().default("api"),
|
origin: workflowRunOrigin("origin").notNull().default("api"),
|
||||||
status: workflowRunStatus("status").notNull().default("not-started"),
|
status: workflowRunStatus("status").notNull().default("not-started"),
|
||||||
ended_at: timestamp("ended_at"),
|
ended_at: timestamp("ended_at"),
|
||||||
// comfy deploy run created time
|
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
// modal gpu cold start begin
|
|
||||||
queued_at: timestamp("queued_at"),
|
|
||||||
// modal gpu function actual start time
|
|
||||||
started_at: timestamp("started_at"),
|
started_at: timestamp("started_at"),
|
||||||
gpu: machineGPUOptions("gpu"),
|
|
||||||
machine_type: machinesType("machine_type"),
|
|
||||||
user_id: text("user_id"),
|
|
||||||
org_id: text("org_id"),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const workflowRunRelations = relations(
|
export const workflowRunRelations = relations(
|
||||||
@@ -349,19 +334,6 @@ export const apiKeyTable = dbSchema.table("api_keys", {
|
|||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userUsageTable = dbSchema.table("user_usage", {
|
|
||||||
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
|
||||||
org_id: text("org_id"),
|
|
||||||
user_id: text("user_id")
|
|
||||||
.references(() => usersTable.id, {
|
|
||||||
onDelete: "cascade",
|
|
||||||
})
|
|
||||||
.notNull(),
|
|
||||||
usage_time: real("usage_time").default(0).notNull(),
|
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
|
||||||
ended_at: timestamp("ended_at").defaultNow().notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const authRequestsTable = dbSchema.table("auth_requests", {
|
export const authRequestsTable = dbSchema.table("auth_requests", {
|
||||||
request_id: text("request_id").primaryKey().notNull(),
|
request_id: text("request_id").primaryKey().notNull(),
|
||||||
user_id: text("user_id"),
|
user_id: text("user_id"),
|
||||||
@@ -372,130 +344,8 @@ export const authRequestsTable = dbSchema.table("auth_requests", {
|
|||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resourceUpload = pgEnum("resource_upload", [
|
|
||||||
"started",
|
|
||||||
"success",
|
|
||||||
"failed",
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const modelUploadType = pgEnum("model_upload_type", [
|
|
||||||
"civitai",
|
|
||||||
"download-url",
|
|
||||||
"huggingface", // remove?
|
|
||||||
"other",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// https://www.answeroverflow.com/m/1125106227387584552
|
|
||||||
export const modelTypes = [
|
|
||||||
"checkpoint",
|
|
||||||
"lora",
|
|
||||||
"embedding",
|
|
||||||
"vae",
|
|
||||||
] as const
|
|
||||||
export const modelType = pgEnum("model_type", modelTypes);
|
|
||||||
export type modelEnumType = (typeof modelTypes)[number];
|
|
||||||
|
|
||||||
export const modelTable = dbSchema.table("models", {
|
|
||||||
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
|
||||||
user_id: text("user_id").references(() => usersTable.id, {}), // perhaps a "special" user_id for global models
|
|
||||||
org_id: text("org_id"),
|
|
||||||
description: text("description"),
|
|
||||||
|
|
||||||
user_volume_id: uuid("user_volume_id")
|
|
||||||
.notNull()
|
|
||||||
.references(() => userVolume.id, {
|
|
||||||
onDelete: "cascade",
|
|
||||||
}),
|
|
||||||
|
|
||||||
model_name: text("model_name"),
|
|
||||||
folder_path: text("folder_path"), // in volume
|
|
||||||
|
|
||||||
civitai_id: text("civitai_id"),
|
|
||||||
civitai_version_id: text("civitai_version_id"),
|
|
||||||
civitai_url: text("civitai_url"),
|
|
||||||
civitai_download_url: text("civitai_download_url"),
|
|
||||||
civitai_model_response: jsonb("civitai_model_response").$type<
|
|
||||||
z.infer<typeof CivitaiModelResponse>
|
|
||||||
>(),
|
|
||||||
|
|
||||||
// for our own storage
|
|
||||||
hf_url: text("hf_url"),
|
|
||||||
s3_url: text("s3_url"),
|
|
||||||
|
|
||||||
user_url: text("client_url"),
|
|
||||||
|
|
||||||
is_public: boolean("is_public").notNull().default(true),
|
|
||||||
status: resourceUpload("status").notNull().default("started"),
|
|
||||||
upload_machine_id: text("upload_machine_id"), // TODO: review if actually needed
|
|
||||||
upload_type: modelUploadType("upload_type").notNull(),
|
|
||||||
model_type: modelType("model_type").notNull().default("checkpoint"),
|
|
||||||
error_log: text("error_log"),
|
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
|
||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const userVolume = dbSchema.table("user_volume", {
|
|
||||||
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
|
||||||
user_id: text("user_id").references(() => usersTable.id, {
|
|
||||||
// onDelete: "cascade",
|
|
||||||
}),
|
|
||||||
org_id: text("org_id"),
|
|
||||||
volume_name: text("volume_name").notNull(),
|
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
|
||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
|
||||||
disabled: boolean("disabled").default(false).notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const modelRelations = relations(modelTable, ({ one }) => ({
|
|
||||||
user: one(usersTable, {
|
|
||||||
fields: [modelTable.user_id],
|
|
||||||
references: [usersTable.id],
|
|
||||||
}),
|
|
||||||
volume: one(userVolume, {
|
|
||||||
fields: [modelTable.user_volume_id],
|
|
||||||
references: [userVolume.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const modalVolumeRelations = relations(userVolume, ({ many, one }) => ({
|
|
||||||
model: many(modelTable),
|
|
||||||
user: one(usersTable, {
|
|
||||||
fields: [userVolume.user_id],
|
|
||||||
references: [usersTable.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const subscriptionPlan = pgEnum("subscription_plan", [
|
|
||||||
"basic",
|
|
||||||
"pro",
|
|
||||||
"enterprise",
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const subscriptionPlanStatus = pgEnum("subscription_plan_status", [
|
|
||||||
"active",
|
|
||||||
"deleted",
|
|
||||||
"paused",
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const subscriptionStatusTable = dbSchema.table("subscription_status", {
|
|
||||||
stripe_customer_id: text("stripe_customer_id").primaryKey().notNull(),
|
|
||||||
user_id: text("user_id"),
|
|
||||||
org_id: text("org_id"),
|
|
||||||
plan: subscriptionPlan("plan").notNull(),
|
|
||||||
status: subscriptionPlanStatus("status").notNull(),
|
|
||||||
subscription_id: text("subscription_id"),
|
|
||||||
subscription_item_plan_id: text("subscription_item_plan_id"),
|
|
||||||
subscription_item_api_id: text("subscription_item_api_id"),
|
|
||||||
cancel_at_period_end: boolean("cancel_at_period_end").default(false),
|
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
|
||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type UserType = InferSelectModel<typeof usersTable>;
|
export type UserType = InferSelectModel<typeof usersTable>;
|
||||||
export type WorkflowType = InferSelectModel<typeof workflowTable>;
|
export type WorkflowType = InferSelectModel<typeof workflowTable>;
|
||||||
export type MachineType = InferSelectModel<typeof machinesTable>;
|
export type MachineType = InferSelectModel<typeof machinesTable>;
|
||||||
export type WorkflowVersionType = InferSelectModel<typeof workflowVersionTable>;
|
export type WorkflowVersionType = InferSelectModel<typeof workflowVersionTable>;
|
||||||
export type DeploymentType = InferSelectModel<typeof deploymentsTable>;
|
export type DeploymentType = InferSelectModel<typeof deploymentsTable>;
|
||||||
export type ModelType = InferSelectModel<typeof modelTable>;
|
|
||||||
export type UserVolumeType = InferSelectModel<typeof userVolume>;
|
|
||||||
export type UserUsageType = InferSelectModel<typeof userUsageTable>;
|
|
||||||
|
|||||||
@@ -12,21 +12,13 @@ export function getRelativeTime(time: string | Date | null | undefined) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatDuration(seconds: number) {
|
function formatDuration(seconds: number) {
|
||||||
const hours = Math.floor(seconds / 3600);
|
const minutes = Math.floor(seconds / 60);
|
||||||
const minutes = Math.floor((seconds % 3600) / 60);
|
|
||||||
const remainingSeconds = seconds % 60;
|
const remainingSeconds = seconds % 60;
|
||||||
|
|
||||||
let result = "";
|
|
||||||
if (hours > 0) {
|
|
||||||
result += `${hours} hrs `;
|
|
||||||
}
|
|
||||||
if (minutes > 0) {
|
if (minutes > 0) {
|
||||||
result += `${minutes} mins `;
|
return `${minutes}.${remainingSeconds} mins`;
|
||||||
|
} else {
|
||||||
|
return `${remainingSeconds.toFixed(1)} secs`;
|
||||||
}
|
}
|
||||||
if (remainingSeconds > 0) {
|
|
||||||
result += `${remainingSeconds.toFixed(1)} secs`;
|
|
||||||
}
|
|
||||||
return result.trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDuration(durationInSecs: number) {
|
export function getDuration(durationInSecs: number) {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export const registerWorkflowUploadRoute = (app: App) => {
|
|||||||
workflow_id = _workflow_id;
|
workflow_id = _workflow_id;
|
||||||
version = _version;
|
version = _version;
|
||||||
} else if (workflow_id) {
|
} else if (workflow_id) {
|
||||||
const workflow = await db
|
const _workflow = await db
|
||||||
.select()
|
.select()
|
||||||
.from(workflowTable)
|
.from(workflowTable)
|
||||||
.where(
|
.where(
|
||||||
@@ -126,7 +126,7 @@ export const registerWorkflowUploadRoute = (app: App) => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (workflow.length === 0) {
|
if (_workflow.length === 0) {
|
||||||
return c.json(
|
return c.json(
|
||||||
{
|
{
|
||||||
error: "Invalid workflow_id",
|
error: "Invalid workflow_id",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|||||||
export const APIKeyBodyRequest = z.object({
|
export const APIKeyBodyRequest = z.object({
|
||||||
user_id: z.string().optional().nullable(),
|
user_id: z.string().optional().nullable(),
|
||||||
org_id: z.string().optional().nullable(),
|
org_id: z.string().optional().nullable(),
|
||||||
iat: z.number().optional(),
|
iat: z.number(),
|
||||||
exp: z.number().optional(),
|
exp: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
import { modelTypes } from "@/db/schema";
|
|
||||||
|
|
||||||
export const downloadUrlModelSchema = z.object({
|
|
||||||
url: z.string().url(),
|
|
||||||
model_type: z.enum(modelTypes).default("checkpoint")
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -66,12 +66,7 @@ export const createRun = withServerPromise(
|
|||||||
throw new Error("Workflow version not found");
|
throw new Error("Workflow version not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
let { userId, orgId } = auth();
|
if (apiUser)
|
||||||
|
|
||||||
// If is API user, check if they have access to the workflow
|
|
||||||
if (apiUser) {
|
|
||||||
userId = apiUser.user_id ?? null;
|
|
||||||
orgId = apiUser.org_id;
|
|
||||||
if (apiUser.org_id) {
|
if (apiUser.org_id) {
|
||||||
// is org api call, check org only
|
// is org api call, check org only
|
||||||
if (apiUser.org_id != workflow_version_data.workflow.org_id) {
|
if (apiUser.org_id != workflow_version_data.workflow.org_id) {
|
||||||
@@ -86,7 +81,6 @@ export const createRun = withServerPromise(
|
|||||||
throw new Error("Workflow not found");
|
throw new Error("Workflow not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const workflow_api = workflow_version_data.workflow_api;
|
const workflow_api = workflow_version_data.workflow_api;
|
||||||
|
|
||||||
@@ -96,7 +90,12 @@ export const createRun = withServerPromise(
|
|||||||
Object.entries(workflow_api).forEach(([_, node]) => {
|
Object.entries(workflow_api).forEach(([_, node]) => {
|
||||||
if (node.inputs["input_id"] === key) {
|
if (node.inputs["input_id"] === key) {
|
||||||
node.inputs["input_id"] = inputs[key];
|
node.inputs["input_id"] = inputs[key];
|
||||||
|
// Fix for external text default value
|
||||||
|
if (node.class_type == "ComfyUIDeployExternalText") {
|
||||||
|
node.inputs["default_value"] = inputs[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,10 +119,6 @@ export const createRun = withServerPromise(
|
|||||||
workflow_inputs: inputs,
|
workflow_inputs: inputs,
|
||||||
machine_id: machine.id,
|
machine_id: machine.id,
|
||||||
origin: runOrigin,
|
origin: runOrigin,
|
||||||
org_id: orgId,
|
|
||||||
user_id: userId,
|
|
||||||
gpu: machine.gpu,
|
|
||||||
machine_type: machine.type,
|
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
@@ -148,14 +143,14 @@ export const createRun = withServerPromise(
|
|||||||
body: JSON.stringify(_data),
|
body: JSON.stringify(_data),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
});
|
});
|
||||||
// console.log(___result);
|
console.log(___result);
|
||||||
if (!___result.ok)
|
if (!___result.ok)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error creating run, ${
|
`Error creating run, ${
|
||||||
___result.statusText
|
___result.statusText
|
||||||
} ${await ___result.text()}`,
|
} ${await ___result.text()}`,
|
||||||
);
|
);
|
||||||
// console.log(_data, ___result);
|
console.log(_data, ___result);
|
||||||
break;
|
break;
|
||||||
case "runpod-serverless":
|
case "runpod-serverless":
|
||||||
const data = {
|
const data = {
|
||||||
@@ -182,14 +177,14 @@ export const createRun = withServerPromise(
|
|||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
});
|
});
|
||||||
// console.log(__result);
|
console.log(__result);
|
||||||
if (!__result.ok)
|
if (!__result.ok)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error creating run, ${
|
`Error creating run, ${
|
||||||
__result.statusText
|
__result.statusText
|
||||||
} ${await __result.text()}`,
|
} ${await __result.text()}`,
|
||||||
);
|
);
|
||||||
// console.log(data, __result);
|
console.log(data, __result);
|
||||||
break;
|
break;
|
||||||
case "classic":
|
case "classic":
|
||||||
const body = {
|
const body = {
|
||||||
@@ -229,6 +224,15 @@ export const createRun = withServerPromise(
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It successfully started, update the started_at time
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(workflowRunsTable)
|
||||||
|
.set({
|
||||||
|
started_at: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(workflowRunsTable.id, workflow_run[0].id));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
workflow_run_id: workflow_run[0].id,
|
workflow_run_id: workflow_run[0].id,
|
||||||
message: "Successful workflow run",
|
message: "Successful workflow run",
|
||||||
|
|||||||