Compare commits
136
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb5e2f36cd | ||
|
|
0b0015fbd1 | ||
|
|
abb4a4798c | ||
|
|
95238469d6 | ||
|
|
86f14b5bce | ||
|
|
84802f5c4e | ||
|
|
68b4d93639 | ||
|
|
5d349153d2 | ||
|
|
ea675fcd4c | ||
|
|
4c715b815a | ||
|
|
bef6ce35de | ||
|
|
d8951df35f | ||
|
|
38fea1e79f | ||
|
|
66ad3ab4c2 | ||
|
|
488d2aee8c | ||
|
|
c4628f6e4c | ||
|
|
9ba349d36a | ||
|
|
08ab93127e | ||
|
|
de641f0acf | ||
|
|
d4d7e98487 | ||
|
|
967a77d3a3 | ||
|
|
a4cd5db360 | ||
|
|
576a6744a4 | ||
|
|
3e5ff7702e | ||
|
|
009589630d | ||
|
|
8eb2ce3e10 | ||
|
|
e3a1d24304 | ||
|
|
2d033570f4 | ||
|
|
7ae25aa162 | ||
|
|
4b37de9ec5 | ||
|
|
b0d1bcc303 | ||
|
|
2193dd287d | ||
|
|
81bde40aeb | ||
|
|
317f699c46 | ||
|
|
0d1bb2aaf4 | ||
|
|
b0b23783ba | ||
|
|
eeb7310955 | ||
|
|
e40cc5373f | ||
|
|
debdaf418c | ||
|
|
90107ebe1b | ||
|
|
a90c6c1db4 | ||
|
|
1bf3c1dcd0 | ||
|
|
24e95a1954 | ||
|
|
194715920e | ||
|
|
0cb1e92f4f | ||
|
|
d96811a0c3 | ||
|
|
757c587901 | ||
|
|
3b7db4480b | ||
|
|
10bbb393a7 | ||
|
|
c1fc06fd39 | ||
|
|
9de266fbab | ||
|
|
42aaf1acb9 | ||
|
|
852d889397 | ||
|
|
cb01c896a0 | ||
|
|
fbb7b18273 | ||
|
|
5443dcd20b | ||
|
|
e5097a5ed5 | ||
|
|
9a1ab21e91 | ||
|
|
d2dbe3410f | ||
|
|
0ce07c88f8 | ||
|
|
c7727fc1be | ||
|
|
72325a4217 | ||
|
|
5eba792579 | ||
|
|
fe8f5a49ff | ||
|
|
f4d93a82e2 | ||
|
|
18c410ba4e | ||
|
|
6aa9a44f4b | ||
|
|
d3e1ca1b0b | ||
|
|
8f7f06470c | ||
|
|
2a55b20887 | ||
|
|
dd0af2a3e9 | ||
|
|
9a715e9766 | ||
|
|
ac5aba2aa9 | ||
|
|
95e454e687 | ||
|
|
8b790fe7c5 | ||
|
|
9e07ccadb4 | ||
|
|
954de12d08 | ||
|
|
731bdf3982 | ||
|
|
97aa88eac8 | ||
|
|
d7ee49aa49 | ||
|
|
2bace09f5e | ||
|
|
78721b3129 | ||
|
|
112c6bb0f6 | ||
|
|
0311638ab1 | ||
|
|
15e1b1ccb4 | ||
|
|
4bca9a7376 | ||
|
|
e95811474e | ||
|
|
cc03c780e8 | ||
|
|
93b1fa9c79 | ||
|
|
85477aba9d | ||
|
|
62a69dba06 | ||
|
|
8305134a8e | ||
|
|
6f0499c657 | ||
|
|
b968fded32 | ||
|
|
aa419c80f1 | ||
|
|
3c1e49b451 | ||
|
|
72312c7a40 | ||
|
|
f22f5eef4e | ||
|
|
982ef0780d | ||
|
|
a295df973f | ||
|
|
d8df580339 | ||
|
|
0d88907cfd | ||
|
|
4c047aea62 | ||
|
|
db04d02d34 | ||
|
|
3d2eacccc9 | ||
|
|
85e4219ea7 | ||
|
|
df4dfba31d | ||
|
|
911cc8d16b | ||
|
|
224b006ec2 | ||
|
|
dfd139294f | ||
|
|
eb04f246a4 | ||
|
|
d644edd3c4 | ||
|
|
1143b469ad | ||
|
|
0f9e0c9c76 | ||
|
|
763d2f77ce | ||
|
|
b1e9bcc4e6 | ||
|
|
f1b68164c6 | ||
|
|
6437de4def | ||
|
|
b24bdb070e | ||
|
|
14c3ca6bf5 | ||
|
|
7a7ced3e08 | ||
|
|
33b57aea34 | ||
|
|
71dcbe0d11 | ||
|
|
8e3ee6468f | ||
|
|
b8f39239ba | ||
|
|
9296f8f703 | ||
|
|
6ed7807081 | ||
|
|
43fe0a384a | ||
|
|
dae6e46a8e | ||
|
|
f7f3d9708b | ||
|
|
52d6e07eeb | ||
|
|
f64c242ee5 | ||
|
|
c0450b58d5 | ||
|
|
47168930dc | ||
|
|
3043093d22 | ||
|
|
271552b453 |
@@ -4,4 +4,20 @@ FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:${VARIANT}
|
||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||
|
||||
WORKDIR "/"
|
||||
|
||||
# Install fly
|
||||
RUN curl -L https://fly.io/install.sh | sh
|
||||
|
||||
ENV FLYCTL_INSTALL="/root/.fly"
|
||||
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 PATH="$FLYCTL_INSTALL/bin:$PATH"' >> ~/.bashrc
|
||||
|
||||
RUN npm install -g bun
|
||||
@@ -1,17 +1,22 @@
|
||||
{
|
||||
"name": "Comfy Deploy Dev",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||
"postCreateCommand": "cd web && bun install && bun run migrate-local",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"biomejs.biome",
|
||||
"formulahendry.auto-rename-tag",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"stivo.tailwind-fold"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
"name": "Comfy Deploy Dev",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||
"postCreateCommand": "cd web && bun install && bun run migrate-local",
|
||||
"remoteUser": "root",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"biomejs.biome",
|
||||
"formulahendry.auto-rename-tag",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"stivo.tailwind-fold",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"GitHub.copilot",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"ms-python.python"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,2 +1,3 @@
|
||||
__pycache__
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.vercel
|
||||
|
||||
Vendored
-6
@@ -1,13 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"DavidAnson.vscode-markdownlint", // markdown linting
|
||||
"esbenp.prettier-vscode", // prettier plugin
|
||||
"dbaeumer.vscode-eslint", // eslint plugin
|
||||
"bradlc.vscode-tailwindcss", // hinting / autocompletion for tailwind
|
||||
"ban.spellright", // Spell check for docs
|
||||
"stripe.vscode-stripe", // stripe VSCode extension
|
||||
"rebornix.project-snippets", // Share useful snippets between collaborators
|
||||
"inlang.vs-code-extension",
|
||||
"biomejs.biome" // improved i18n DX
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+4
-7
@@ -2,18 +2,15 @@
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"quickfix.biome": "explicit",
|
||||
"source.organizeImports.biome": "explicit"
|
||||
"quickfix.biome": "always"
|
||||
// "source.organizeImports.biome": "explicit"
|
||||
},
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"spellright.language": ["en"],
|
||||
"spellright.documentTypes": ["markdown", "typescript", "typescriptreact"],
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
[
|
||||
"cva\\(([^)]*)\\)",
|
||||
"[\"'`]([^\"'`]*).*?[\"'`]"
|
||||
]
|
||||
],
|
||||
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||
]
|
||||
// "eslint.workingDirectories": [
|
||||
// { "mode": "auto" }
|
||||
// ],
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.5.2/schema.json",
|
||||
"$schema": "./web/node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"formatter": {
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
MODAL_TOKEN_ID=
|
||||
MODAL_TOKEN_SECRET=
|
||||
CIVITAI_API_KEY=
|
||||
|
||||
# On production set to False
|
||||
DEPLOY_TEST_FLAG=True
|
||||
DEPLOY_TEST_FLAG=True
|
||||
CIVITAI_API_KEY=
|
||||
PUBLIC_MODEL_VOLUME_NAME=
|
||||
|
||||
@@ -13,4 +13,4 @@ RUN mkdir builds
|
||||
# CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "80", "--lifespan", "on"]
|
||||
CMD ["python", "src/main.py"]
|
||||
# If running behind a proxy like Nginx or Traefik add --proxy-headers
|
||||
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]
|
||||
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]
|
||||
|
||||
@@ -42,6 +42,9 @@ fly secrets set MODAL_TOKEN_SECRET=
|
||||
|
||||
## 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
|
||||
app = <APP_NAME>
|
||||
@@ -53,5 +56,5 @@ fly launch
|
||||
```
|
||||
if not, run this instead
|
||||
```
|
||||
fly deploy
|
||||
```
|
||||
fly deploy -c "toml file"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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 on 2024-01-03T22:29:34+08:00
|
||||
# fly.toml app configuration file generated for modal-builder-preview on 2024-01-25T09:26:31Z
|
||||
#
|
||||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||
#
|
||||
|
||||
app = "modal-builder"
|
||||
primary_region = "sea"
|
||||
app = 'modal-builder-preview'
|
||||
primary_region = 'sea'
|
||||
|
||||
[build]
|
||||
|
||||
@@ -14,4 +14,9 @@ primary_region = "sea"
|
||||
auto_stop_machines = true
|
||||
auto_start_machines = true
|
||||
min_machines_running = 0
|
||||
processes = ["app"]
|
||||
processes = ['app']
|
||||
|
||||
[[vm]]
|
||||
cpu_kind = 'shared'
|
||||
cpus = 1
|
||||
memory_mb = 1024
|
||||
|
||||
@@ -8,6 +8,7 @@ from enum import Enum
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
from uuid import uuid4
|
||||
from contextlib import asynccontextmanager
|
||||
import asyncio
|
||||
import threading
|
||||
@@ -18,6 +19,7 @@ import requests
|
||||
from urllib.parse import parse_qs
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.types import ASGIApp, Scope, Receive, Send
|
||||
import modal
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
@@ -45,6 +47,8 @@ machine_id_websocket_dict = {}
|
||||
machine_id_status = {}
|
||||
|
||||
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):
|
||||
@@ -139,6 +143,7 @@ def read_root():
|
||||
class GitCustomNodes(BaseModel):
|
||||
hash: str
|
||||
disabled: bool
|
||||
pip: Optional[List[str]] = None
|
||||
|
||||
class FileCustomNodes(BaseModel):
|
||||
filename: str
|
||||
@@ -174,6 +179,9 @@ class Item(BaseModel):
|
||||
snapshot: Snapshot
|
||||
models: List[Model]
|
||||
callback_url: str
|
||||
model_volume_name: str
|
||||
run_timeout: Optional[int] = Field(default=60 * 5)
|
||||
idle_timeout: Optional[int] = Field(default=60)
|
||||
gpu: GPUType = Field(default=GPUType.T4)
|
||||
|
||||
@field_validator('gpu')
|
||||
@@ -223,6 +231,127 @@ async def websocket_endpoint(websocket: WebSocket, machine_id: str):
|
||||
|
||||
# return {"Hello": "World"}
|
||||
|
||||
# definition based on web schema
|
||||
class UploadType(str, Enum):
|
||||
checkpoint = "checkpoint"
|
||||
lora = "lora"
|
||||
embedding = "embedding"
|
||||
clip = "clip"
|
||||
clip_vision = "clip_vision"
|
||||
configs = "configs"
|
||||
controlnet = "controlnet"
|
||||
upscale_models = "upscale_models"
|
||||
vae = "vae"
|
||||
ipadapter = "ipadapter"
|
||||
other = "other"
|
||||
|
||||
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",
|
||||
UploadType.clip: "clip",
|
||||
UploadType.clip_vision: "clip_vision",
|
||||
UploadType.configs: "configs",
|
||||
UploadType.controlnet: "controlnet",
|
||||
UploadType.upscale_models: "upscale_models",
|
||||
UploadType.vae: "vae",
|
||||
UploadType.ipadapter: "ipadapter",
|
||||
UploadType.other: "",
|
||||
}
|
||||
|
||||
class DeleteBody(BaseModel):
|
||||
volume_name: str
|
||||
path: str
|
||||
file_name: str
|
||||
|
||||
|
||||
@app.post("/delete-volume-model")
|
||||
async def delete_model(body: DeleteBody):
|
||||
global last_activity_time
|
||||
last_activity_time = time.time()
|
||||
logger.info(f"Extended inactivity time to {global_timeout}")
|
||||
|
||||
full_path = f"{body.path.rstrip('/')}/{body.file_name}"
|
||||
|
||||
rm_process = await asyncio.subprocess.create_subprocess_exec("modal", "volume", "rm", body.volume_name, full_path,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,)
|
||||
await rm_process.wait()
|
||||
|
||||
logger.info(f"Successfully deleted: {full_path} from volume: {body.volume_name}")
|
||||
stdout, stderr = await rm_process.communicate()
|
||||
if stdout:
|
||||
logger.info(f"cp_process stdout: {stdout.decode()}")
|
||||
if stderr:
|
||||
logger.info(f"cp_process stderr: {stderr.decode()}")
|
||||
|
||||
if rm_process.returncode == 0:
|
||||
return JSONResponse(status_code=200, content={"status":f"Successfully deleted {full_path} from volume {body.volume_name}"})
|
||||
else:
|
||||
return JSONResponse(status_code=500, content={"status": "error", "error": stderr.decode()})
|
||||
|
||||
@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}-{uuid4()}"
|
||||
|
||||
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]
|
||||
if upload_path == "":
|
||||
# TODO: deal with custom paths
|
||||
pass
|
||||
|
||||
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'),
|
||||
"app_name": f"vol_name_{uuid4()}"
|
||||
}
|
||||
with open(f"{folder_path}/config.py", "w") as f:
|
||||
f.write("config = " + json.dumps(config))
|
||||
|
||||
process = await asyncio.subprocess.create_subprocess_shell(
|
||||
f"python runner.py",
|
||||
cwd=folder_path,
|
||||
env={**os.environ, "COLUMNS": "10000"}
|
||||
)
|
||||
await process.wait()
|
||||
|
||||
# import modal
|
||||
# modal.deploy_stub(stub)
|
||||
# stub["download_model"].web_url
|
||||
|
||||
@app.post("/create")
|
||||
async def create_machine(item: Item):
|
||||
@@ -308,11 +437,21 @@ async def build_logic(item: Item):
|
||||
cp_process = await asyncio.subprocess.create_subprocess_exec("cp", "-r", "/app/src/template", folder_path)
|
||||
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
|
||||
config = {
|
||||
"name": item.name,
|
||||
"deploy_test": os.environ.get("DEPLOY_TEST_FLAG", "False"),
|
||||
"gpu": item.gpu
|
||||
"gpu": item.gpu,
|
||||
"public_model_volume": public_model_volume_name,
|
||||
"private_model_volume": item.model_volume_name,
|
||||
"pip": list(pip_modules),
|
||||
"run_timeout": item.run_timeout,
|
||||
"idle_timeout": item.idle_timeout,
|
||||
}
|
||||
with open(f"{folder_path}/config.py", "w") as f:
|
||||
f.write("config = " + json.dumps(config))
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from config import config
|
||||
import modal
|
||||
from modal import Image, Mount, web_endpoint, Stub, asgi_app
|
||||
from modal import Image, Mount, web_endpoint, Stub, asgi_app, method, enter, exit
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
from pydantic import BaseModel
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from volume_setup import volumes
|
||||
from datetime import datetime
|
||||
import aiohttp
|
||||
from aiohttp import TCPConnector
|
||||
# deploy_test = False
|
||||
|
||||
import os
|
||||
@@ -27,15 +30,17 @@ deploy_test = config["deploy_test"] == "True"
|
||||
web_app = FastAPI()
|
||||
print(config)
|
||||
print("deploy_test ", deploy_test)
|
||||
print('volumes', volumes)
|
||||
stub = Stub(name=config["name"])
|
||||
# print(stub.app_id)
|
||||
|
||||
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"))
|
||||
|
||||
dockerfile_image = (
|
||||
modal.Image.debian_slim()
|
||||
modal.Image.debian_slim(
|
||||
python_version="3.11.1"
|
||||
)
|
||||
.apt_install("git", "wget")
|
||||
.pip_install(
|
||||
"git+https://github.com/modal-labs/asgiproxy.git", "httpx", "tqdm"
|
||||
@@ -48,20 +53,24 @@ if not deploy_test:
|
||||
|
||||
# Install comfyui manager
|
||||
"cd /comfyui/custom_nodes && git clone https://github.com/ltdrdata/ComfyUI-Manager.git",
|
||||
"cd /comfyui/custom_nodes/ComfyUI-Manager && git reset --hard 9c86f62b912f4625fe2b929c7fc61deb9d16f6d3",
|
||||
"cd /comfyui/custom_nodes/ComfyUI-Manager && git reset --hard 8dd801435bb75aa1d24b7e382bac070a4c18bc51",
|
||||
"cd /comfyui/custom_nodes/ComfyUI-Manager && pip install -r requirements.txt",
|
||||
"cd /comfyui/custom_nodes/ComfyUI-Manager && mkdir startup-scripts",
|
||||
)
|
||||
# .run_commands(f"cat /comfyui/server.py")
|
||||
# .run_commands(f"ls /comfyui/app")
|
||||
# .run_commands(
|
||||
# # Install comfy deploy
|
||||
# "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")
|
||||
.run_commands("chmod +x /start.sh")
|
||||
|
||||
# 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/snapshot.json", "/comfyui/custom_nodes/ComfyUI-Manager/startup-scripts/restore-snapshot.json")
|
||||
.run_commands("python restore_snapshot.py")
|
||||
@@ -77,7 +86,7 @@ if not deploy_test:
|
||||
# Time to wait between API check attempts in milliseconds
|
||||
COMFY_API_AVAILABLE_INTERVAL_MS = 50
|
||||
# Maximum number of API check attempts
|
||||
COMFY_API_AVAILABLE_MAX_RETRIES = 500
|
||||
COMFY_API_AVAILABLE_MAX_RETRIES = 1000
|
||||
# Time to wait between poll attempts in milliseconds
|
||||
COMFY_POLLING_INTERVAL_MS = 250
|
||||
# Maximum number of poll attempts
|
||||
@@ -86,48 +95,34 @@ COMFY_POLLING_MAX_RETRIES = 1000
|
||||
COMFY_HOST = "127.0.0.1:8188"
|
||||
|
||||
|
||||
def check_server(url, retries=50, delay=500):
|
||||
import requests
|
||||
import time
|
||||
"""
|
||||
Check if a server is reachable via HTTP GET request
|
||||
|
||||
Args:
|
||||
- url (str): The URL to check
|
||||
- retries (int, optional): The number of times to attempt connecting to the server. Default is 50
|
||||
- delay (int, optional): The time in milliseconds to wait between retries. Default is 500
|
||||
|
||||
Returns:
|
||||
bool: True if the server is reachable within the given number of retries, otherwise False
|
||||
"""
|
||||
|
||||
for i in range(retries):
|
||||
async def check_server(url, retries=50, delay=500):
|
||||
import aiohttp
|
||||
# for i in range(retries):
|
||||
while True:
|
||||
try:
|
||||
response = requests.get(url)
|
||||
|
||||
# If the response status code is 200, the server is up and running
|
||||
if response.status_code == 200:
|
||||
print(f"runpod-worker-comfy - API is reachable")
|
||||
return True
|
||||
except requests.RequestException as e:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as response:
|
||||
# If the response status code is 200, the server is up and running
|
||||
if response.status == 200:
|
||||
print(f"comfy-modal - API is reachable")
|
||||
return True
|
||||
except Exception as e:
|
||||
# If an exception occurs, the server may not be ready
|
||||
pass
|
||||
|
||||
# print(f"runpod-worker-comfy - trying")
|
||||
|
||||
# Wait for the specified delay before retrying
|
||||
time.sleep(delay / 1000)
|
||||
await asyncio.sleep(delay / 1000)
|
||||
|
||||
print(
|
||||
f"runpod-worker-comfy - Failed to connect to server at {url} after {retries} attempts."
|
||||
f"comfy-modal - Failed to connect to server at {url} after {retries} attempts."
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def check_status(prompt_id):
|
||||
req = urllib.request.Request(
|
||||
f"http://{COMFY_HOST}/comfyui-deploy/check-status?prompt_id={prompt_id}")
|
||||
return json.loads(urllib.request.urlopen(req).read())
|
||||
async def check_status(prompt_id):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"http://{COMFY_HOST}/comfyui-deploy/check-status?prompt_id={prompt_id}") as response:
|
||||
return await response.json()
|
||||
|
||||
|
||||
class Input(BaseModel):
|
||||
@@ -137,12 +132,12 @@ class Input(BaseModel):
|
||||
file_upload_endpoint: str
|
||||
|
||||
|
||||
def queue_workflow_comfy_deploy(data: Input):
|
||||
async def queue_workflow_comfy_deploy(data: Input):
|
||||
data_str = data.json()
|
||||
data_bytes = data_str.encode('utf-8')
|
||||
req = urllib.request.Request(
|
||||
f"http://{COMFY_HOST}/comfyui-deploy/run", data=data_bytes)
|
||||
return json.loads(urllib.request.urlopen(req).read())
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(f"http://{COMFY_HOST}/comfyui-deploy/run", data=data_bytes) as response:
|
||||
return await response.json()
|
||||
|
||||
|
||||
class RequestInput(BaseModel):
|
||||
@@ -153,89 +148,253 @@ image = Image.debian_slim()
|
||||
|
||||
target_image = image if deploy_test else dockerfile_image
|
||||
|
||||
run_timeout = config["run_timeout"]
|
||||
idle_timeout = config["idle_timeout"]
|
||||
|
||||
@stub.function(image=target_image, gpu=config["gpu"])
|
||||
def run(input: Input):
|
||||
import subprocess
|
||||
import time
|
||||
# Make sure that the ComfyUI API is available
|
||||
print(f"comfy-modal - check server")
|
||||
import asyncio
|
||||
|
||||
command = ["python", "main.py",
|
||||
"--disable-auto-launch", "--disable-metadata"]
|
||||
server_process = subprocess.Popen(command, cwd="/comfyui")
|
||||
@stub.cls(
|
||||
image=target_image,
|
||||
gpu=config["gpu"] ,
|
||||
volumes=volumes,
|
||||
timeout=(config["run_timeout"] + 20),
|
||||
container_idle_timeout=config["idle_timeout"],
|
||||
allow_concurrent_inputs=1,
|
||||
)
|
||||
class ComfyDeployRunner:
|
||||
|
||||
check_server(
|
||||
f"http://{COMFY_HOST}",
|
||||
COMFY_API_AVAILABLE_MAX_RETRIES,
|
||||
COMFY_API_AVAILABLE_INTERVAL_MS,
|
||||
)
|
||||
machine_logs = []
|
||||
|
||||
job_input = input
|
||||
async def read_stream(self, stream, isStderr):
|
||||
import time
|
||||
while True:
|
||||
try:
|
||||
line = await stream.readline()
|
||||
if line:
|
||||
l = line.decode('utf-8').strip()
|
||||
|
||||
# print(f"comfy-modal - got input {job_input}")
|
||||
if l == "":
|
||||
continue
|
||||
|
||||
# Queue the workflow
|
||||
try:
|
||||
# job_input is the json input
|
||||
queued_workflow = queue_workflow_comfy_deploy(
|
||||
job_input) # queue_workflow(workflow)
|
||||
prompt_id = queued_workflow["prompt_id"]
|
||||
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)}"}
|
||||
if not isStderr:
|
||||
print(l, flush=True)
|
||||
self.machine_logs.append({
|
||||
"logs": l,
|
||||
"timestamp": time.time()
|
||||
})
|
||||
|
||||
# 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)
|
||||
else:
|
||||
# is error
|
||||
# logger.error(l)
|
||||
print(l, flush=True)
|
||||
self.machine_logs.append({
|
||||
"logs": l,
|
||||
"timestamp": time.time()
|
||||
})
|
||||
else:
|
||||
break
|
||||
except asyncio.CancelledError:
|
||||
# Handle the cancellation here if needed
|
||||
break # Break out of the loop on cancellation
|
||||
|
||||
# Exit the loop if we have found the history
|
||||
# if prompt_id in history and history[prompt_id].get("outputs"):
|
||||
# break
|
||||
@enter()
|
||||
async def setup(self):
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
# 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
|
||||
# Make sure that the ComfyUI API is available
|
||||
print(f"comfy-modal - check server")
|
||||
|
||||
self.server_process = await asyncio.subprocess.create_subprocess_shell(
|
||||
f"python main.py --disable-auto-launch --disable-metadata",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd="/comfyui",
|
||||
# env={**os.environ, "COLUMNS": "10000"}
|
||||
)
|
||||
|
||||
@exit()
|
||||
async def cleanup(self, exc_type, exc_value, traceback):
|
||||
print(f"comfy-modal - cleanup", exc_type, exc_value, traceback)
|
||||
# Get the current event loop
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# Check if the event loop is closed
|
||||
if loop.is_closed():
|
||||
print("The event loop is closed.")
|
||||
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)}"}
|
||||
try:
|
||||
self.server_process.terminate()
|
||||
await self.server_process.wait()
|
||||
except Exception as e:
|
||||
print("Issues when cleaning up", e)
|
||||
print("The event loop is open.")
|
||||
|
||||
print(f"comfy-modal - Finished, turning off")
|
||||
server_process.terminate()
|
||||
@method()
|
||||
async def run(self, input: Input):
|
||||
import signal
|
||||
import time
|
||||
import aiohttp
|
||||
|
||||
# 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}
|
||||
stdout_task = asyncio.create_task(
|
||||
self.read_stream(self.server_process.stdout, False))
|
||||
stderr_task = asyncio.create_task(
|
||||
self.read_stream(self.server_process.stderr, True))
|
||||
|
||||
try:
|
||||
class TimeoutError(Exception):
|
||||
pass
|
||||
|
||||
return result
|
||||
print("Running remotely on Modal!")
|
||||
def timeout_handler(signum, frame):
|
||||
data = json.dumps({
|
||||
"run_id": input.prompt_id,
|
||||
"status": "timeout",
|
||||
"time": datetime.now().isoformat()
|
||||
}).encode('utf-8')
|
||||
req = urllib.request.Request(input.status_endpoint, data=data, method='POST')
|
||||
urllib.request.urlopen(req)
|
||||
raise TimeoutError("Operation timed out")
|
||||
|
||||
signal.signal(signal.SIGALRM, timeout_handler)
|
||||
|
||||
try:
|
||||
signal.alarm(run_timeout)
|
||||
|
||||
ok = await check_server(
|
||||
f"http://{COMFY_HOST}",
|
||||
COMFY_API_AVAILABLE_MAX_RETRIES,
|
||||
COMFY_API_AVAILABLE_INTERVAL_MS,
|
||||
)
|
||||
|
||||
if not ok:
|
||||
raise Exception("ComfyUI API is not available")
|
||||
# Set an alarm for some seconds in the future
|
||||
|
||||
data = json.dumps({
|
||||
"run_id": input.prompt_id,
|
||||
"status": "started",
|
||||
"time": datetime.now().isoformat()
|
||||
}).encode('utf-8')
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(input.status_endpoint, data=data) as response:
|
||||
pass
|
||||
|
||||
job_input = input
|
||||
|
||||
try:
|
||||
queued_workflow = await queue_workflow_comfy_deploy(job_input) # queue_workflow(workflow)
|
||||
prompt_id = queued_workflow["prompt_id"]
|
||||
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
|
||||
print(f"comfy-modal - wait until image generation is complete")
|
||||
retries = 0
|
||||
status = ""
|
||||
try:
|
||||
print("getting request")
|
||||
# while retries < COMFY_POLLING_MAX_RETRIES:
|
||||
while True:
|
||||
status_result = await check_status(prompt_id=prompt_id)
|
||||
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
|
||||
await asyncio.sleep(COMFY_POLLING_INTERVAL_MS / 1000)
|
||||
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")
|
||||
|
||||
result = {"status": status}
|
||||
|
||||
except TimeoutError:
|
||||
print("Operation timed out")
|
||||
return {"status": "failed"}
|
||||
except Exception as e:
|
||||
print(f"Unexpected error occurred: {str(e)}")
|
||||
data = json.dumps({
|
||||
"run_id": input.prompt_id,
|
||||
"status": "failed",
|
||||
"time": datetime.now().isoformat()
|
||||
}).encode('utf-8')
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(input.status_endpoint, data=data) as response:
|
||||
print("response", response)
|
||||
self.machine_logs.append({
|
||||
"logs": str(e),
|
||||
"timestamp": time.time()
|
||||
})
|
||||
finally:
|
||||
signal.alarm(0)
|
||||
|
||||
print("uploading log_data")
|
||||
data = json.dumps({
|
||||
"run_id": input.prompt_id,
|
||||
"time": datetime.now().isoformat(),
|
||||
"log_data": self.machine_logs
|
||||
}).encode('utf-8')
|
||||
print("my logs", len(self.machine_logs))
|
||||
# Clear logs
|
||||
timeout = aiohttp.ClientTimeout(total=60) # 60 seconds total timeout
|
||||
# Use HTTP/1.1 explicitly and increase the connection pool size
|
||||
connector = TCPConnector(limit=100, force_close=True, enable_cleanup_closed=True)
|
||||
|
||||
async with aiohttp.ClientSession(timeout=timeout, connector=connector) as session:
|
||||
try:
|
||||
async with session.post(input.status_endpoint, data=data) as response:
|
||||
print("response", response)
|
||||
# Process your response here
|
||||
except asyncio.TimeoutError:
|
||||
print("Request timed out")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
print("uploaded log_data")
|
||||
# print(data)
|
||||
self.machine_logs = []
|
||||
finally:
|
||||
stdout_task.cancel()
|
||||
stderr_task.cancel()
|
||||
await stdout_task
|
||||
await stderr_task
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@web_app.post("/run")
|
||||
async def bar(request_input: RequestInput):
|
||||
# print(request_input)
|
||||
async def post_run(request_input: RequestInput):
|
||||
if not deploy_test:
|
||||
return run.remote(request_input.input)
|
||||
# pass
|
||||
# print(request_input.input.prompt_id, request_input.input.status_endpoint)
|
||||
data = json.dumps({
|
||||
"run_id": request_input.input.prompt_id,
|
||||
"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 = await model.run.spawn.aio(request_input.input)
|
||||
|
||||
@stub.function(image=image)
|
||||
print("call", call)
|
||||
|
||||
# call = run.spawn()
|
||||
return {"call_id": None}
|
||||
|
||||
return {"call_id": None}
|
||||
|
||||
@stub.function(image=image
|
||||
,volumes=volumes
|
||||
)
|
||||
@asgi_app()
|
||||
def comfyui_api():
|
||||
return web_app
|
||||
@@ -285,6 +444,7 @@ def spawn_comfyui_in_background():
|
||||
# to be on a single container.
|
||||
concurrency_limit=1,
|
||||
timeout=10 * 60,
|
||||
volumes=volumes,
|
||||
)
|
||||
@asgi_app()
|
||||
def comfyui_app():
|
||||
@@ -303,4 +463,4 @@ def comfyui_app():
|
||||
},
|
||||
)()
|
||||
|
||||
return make_simple_proxy_app(ProxyContext(config))
|
||||
return make_simple_proxy_app(ProxyContext(config))
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
config = {"name": "my-app", "deploy_test": "True", "gpu": "T4"}
|
||||
config = {
|
||||
"name": "my-app",
|
||||
"deploy_test": "False",
|
||||
"gpu": "T4",
|
||||
"public_model_volume": "model-store",
|
||||
"private_model_volume": "private-model-store",
|
||||
"pip": [],
|
||||
"run_timeout": 60 * 5,
|
||||
"idle_timeout": 60
|
||||
}
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
comfyui:
|
||||
base_path: /runpod-volume/ComfyUI/
|
||||
checkpoints: models/checkpoints/
|
||||
clip: models/clip/
|
||||
clip_vision: models/clip_vision/
|
||||
configs: models/configs/
|
||||
controlnet: models/controlnet/
|
||||
embeddings: models/embeddings/
|
||||
loras: models/loras/
|
||||
upscale_models: models/upscale_models/
|
||||
vae: models/vae/
|
||||
public:
|
||||
base_path: /public_models/
|
||||
checkpoints: checkpoints
|
||||
clip: clip
|
||||
clip_vision: clip_vision
|
||||
configs: configs
|
||||
controlnet: controlnet
|
||||
embeddings: embeddings
|
||||
loras: loras
|
||||
upscale_models: upscale_models
|
||||
vae: vae
|
||||
ipadapter: ipadapter
|
||||
|
||||
|
||||
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
|
||||
ipadapter: ipadapter
|
||||
|
||||
@@ -54,4 +54,4 @@ for model in models:
|
||||
|
||||
# Close the server
|
||||
server_process.terminate()
|
||||
print("Finished installing dependencies.")
|
||||
print("Finished installing dependencies.")
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"comfyui": "d0165d819afe76bd4e6bdd710eb5f3e571b6a804",
|
||||
"git_custom_nodes": {},
|
||||
"file_custom_nodes": []
|
||||
}
|
||||
"comfyui": "d0165d819afe76bd4e6bdd710eb5f3e571b6a804",
|
||||
"git_custom_nodes": {
|
||||
"https://github.com/BennyKok/comfyui-deploy.git": {
|
||||
"hash": "df46e3a0e5ad93fa71f5d216997e376af33b2a6d",
|
||||
"disabled": false
|
||||
}
|
||||
},
|
||||
"file_custom_nodes": []
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
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}
|
||||
@@ -0,0 +1,76 @@
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
import modal
|
||||
from config import config
|
||||
import os
|
||||
import subprocess
|
||||
from pprint import pprint
|
||||
|
||||
stub = modal.Stub(config["app_name"])
|
||||
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"]
|
||||
web_app = FastAPI()
|
||||
|
||||
# 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
|
||||
|
||||
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, "-nv"])
|
||||
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.function(image=image)
|
||||
# @modal.asgi_app()
|
||||
def simple_download():
|
||||
import requests
|
||||
try:
|
||||
list(download_model.starmap([(vol_name, download_conf) for vol_name,download_conf 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})
|
||||
@@ -0,0 +1,19 @@
|
||||
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": "",
|
||||
"app_name": "",
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import modal
|
||||
import requests
|
||||
from app import stub
|
||||
from config import config
|
||||
|
||||
modal.runner.deploy_stub(stub)
|
||||
print("deployed stub")
|
||||
# web_url = stub["simple_download"].web_url
|
||||
f = modal.Function.lookup(config['app_name'], "simple_download")
|
||||
f.spawn()
|
||||
# print(f"web_url: {web_url}")
|
||||
# requests.post(web_url)
|
||||
@@ -3,20 +3,20 @@ from PIL import Image, ImageOps
|
||||
import numpy as np
|
||||
import torch
|
||||
import folder_paths
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
class ComfyUIDeployExternalCheckpoints:
|
||||
class ComfyUIDeployExternalCheckpoint:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"input_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "input_checkpoints"},
|
||||
{"multiline": False, "default": "input_checkpoint"},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"default_checkpoints_name": (folder_paths.get_filename_list("checkpoints"), ),
|
||||
"default_checkpoint_name": (folder_paths.get_filename_list("checkpoints"), ),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class ComfyUIDeployExternalCheckpoints:
|
||||
|
||||
CATEGORY = "deploy"
|
||||
|
||||
def run(self, input_id, default_checkpoints_name=None):
|
||||
def run(self, input_id, default_checkpoint_name=None):
|
||||
import requests
|
||||
import os
|
||||
import uuid
|
||||
@@ -39,18 +39,30 @@ class ComfyUIDeployExternalCheckpoints:
|
||||
destination_path = os.path.join(
|
||||
folder_paths.folder_names_and_paths["checkpoints"][0][0], unique_filename)
|
||||
print(destination_path)
|
||||
print("Downloading external checkpoints - " +
|
||||
print("Downloading external checkpoint - " +
|
||||
input_id + " to " + destination_path)
|
||||
response = requests.get(
|
||||
input_id, headers={'User-Agent': 'Mozilla/5.0'}, allow_redirects=True)
|
||||
input_id, headers={'User-Agent': 'Mozilla/5.0'}, allow_redirects=True, stream=True)
|
||||
file_size = int(response.headers.get('Content-Length', 0))
|
||||
chunk = 1
|
||||
chunk_size = 1024
|
||||
num_bars = int(file_size / chunk_size)
|
||||
|
||||
with open(destination_path, 'wb') as out_file:
|
||||
out_file.write(response.content)
|
||||
for chunk in tqdm(
|
||||
response.iter_content(chunk_size=chunk_size),
|
||||
total=num_bars,
|
||||
unit='KB',
|
||||
desc="Downloading",
|
||||
leave=True # leave=True to keep progress bars
|
||||
):
|
||||
out_file.write(chunk)
|
||||
return (unique_filename,)
|
||||
else:
|
||||
return (default_checkpoints_name,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ComfyUIDeployExternalCheckpoints": ComfyUIDeployExternalCheckpoints}
|
||||
"ComfyUIDeployExternalCheckpoint": ComfyUIDeployExternalCheckpoint}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ComfyUIDeployExternalCheckpoints": "External Checkpoints (ComfyUI Deploy)"}
|
||||
"ComfyUIDeployExternalCheckpoint": "External Checkpoint (ComfyUI Deploy)"}
|
||||
|
||||
+69
-16
@@ -26,6 +26,8 @@ import threading
|
||||
api = None
|
||||
api_task = None
|
||||
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):
|
||||
prompt_server = server.PromptServer.instance
|
||||
@@ -157,7 +159,9 @@ async def websocket_handler(request):
|
||||
try:
|
||||
# Send initial state to the new client
|
||||
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:
|
||||
if msg.type == aiohttp.WSMsgType.ERROR:
|
||||
@@ -203,7 +207,10 @@ async def send_json_override(self, event, data, sid=None):
|
||||
prompt_id = data.get('prompt_id')
|
||||
|
||||
# now we send everything
|
||||
await asyncio.wait([send(event, data), self.send_json_original(event, data, sid)])
|
||||
await asyncio.wait([
|
||||
asyncio.create_task(send(event, data)),
|
||||
asyncio.create_task(self.send_json_original(event, data, sid))
|
||||
])
|
||||
|
||||
if event == 'execution_start':
|
||||
update_run(prompt_id, Status.RUNNING)
|
||||
@@ -233,7 +240,12 @@ class Status(Enum):
|
||||
FAILED = "failed"
|
||||
UPLOADING = "uploading"
|
||||
|
||||
# Global variable to keep track of the last read line number
|
||||
last_read_line_number = 0
|
||||
|
||||
def update_run(prompt_id, status: Status):
|
||||
global last_read_line_number
|
||||
|
||||
if prompt_id not in prompt_metadata:
|
||||
return
|
||||
|
||||
@@ -248,16 +260,50 @@ def update_run(prompt_id, status: Status):
|
||||
"run_id": prompt_id,
|
||||
"status": status.value,
|
||||
}
|
||||
prompt_metadata[prompt_id]['status'] = status
|
||||
print(f"Status: {status.value}")
|
||||
|
||||
try:
|
||||
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:
|
||||
error_type = type(e).__name__
|
||||
stack_trace = traceback.format_exc().strip()
|
||||
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"):
|
||||
"""
|
||||
@@ -384,20 +430,25 @@ async def update_file_status(prompt_id, data, uploading, have_error=False, node_
|
||||
"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
|
||||
async def upload_in_background(prompt_id, data, node_id=None, have_upload=True):
|
||||
try:
|
||||
images = data.get('images', [])
|
||||
for image in images:
|
||||
await upload_file(prompt_id, image.get("filename"), subfolder=image.get("subfolder"), type=image.get("type"), content_type=image.get("content_type", "image/png"))
|
||||
|
||||
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"))
|
||||
await handle_upload(prompt_id, data, 'images', "content_type", "image/png")
|
||||
await handle_upload(prompt_id, data, 'files', "content_type", "image/png")
|
||||
# This will also be mp4
|
||||
await handle_upload(prompt_id, data, 'gifs', "format", "image/gif")
|
||||
|
||||
if have_upload:
|
||||
await update_file_status(prompt_id, data, False, node_id=node_id)
|
||||
@@ -438,6 +489,7 @@ prompt_server.send_json = send_json_override.__get__(prompt_server, server.Promp
|
||||
root_path = os.path.dirname(os.path.abspath(__file__))
|
||||
two_dirs_up = os.path.dirname(os.path.dirname(root_path))
|
||||
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
|
||||
|
||||
@@ -477,4 +529,5 @@ def run_in_new_thread(coroutine):
|
||||
t.start()
|
||||
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))
|
||||
|
||||
+43
-32
@@ -7,45 +7,56 @@ import threading
|
||||
import logging
|
||||
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
|
||||
original_stderr = sys.stderr
|
||||
# Check for 'cd-enable-log' flag in input arguments
|
||||
# cd_enable_log = '--cd-enable-log' in sys.argv
|
||||
cd_enable_log = os.environ.get('CD_ENABLE_LOG', 'false').lower() == 'true'
|
||||
|
||||
class StreamToLogger():
|
||||
def __init__(self, log_level):
|
||||
self.log_level = log_level
|
||||
def setup():
|
||||
handler = RotatingFileHandler('comfy-deploy.log', maxBytes=500000, backupCount=5)
|
||||
|
||||
def write(self, buf):
|
||||
if (self.log_level == logging.INFO):
|
||||
original_stdout.write(buf)
|
||||
original_stdout.flush()
|
||||
elif (self.log_level == logging.ERROR):
|
||||
original_stderr.write(buf)
|
||||
original_stderr.flush()
|
||||
original_stdout = sys.stdout
|
||||
original_stderr = sys.stderr
|
||||
|
||||
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
|
||||
class StreamToLogger():
|
||||
def __init__(self, log_level):
|
||||
self.log_level = log_level
|
||||
|
||||
def write(self, buf):
|
||||
if (self.log_level == logging.INFO):
|
||||
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():
|
||||
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):
|
||||
if (self.log_level == logging.INFO):
|
||||
original_stdout.flush()
|
||||
elif (self.log_level == logging.ERROR):
|
||||
original_stderr.flush()
|
||||
def flush(self):
|
||||
if (self.log_level == logging.INFO):
|
||||
original_stdout.flush()
|
||||
elif (self.log_level == logging.ERROR):
|
||||
original_stderr.flush()
|
||||
|
||||
# Redirect stdout and stderr to the logger
|
||||
sys.stdout = StreamToLogger(logging.INFO)
|
||||
sys.stderr = StreamToLogger(logging.ERROR)
|
||||
# Redirect stdout and stderr to the logger
|
||||
sys.stdout = StreamToLogger(logging.INFO)
|
||||
sys.stderr = StreamToLogger(logging.ERROR)
|
||||
|
||||
if cd_enable_log:
|
||||
print("** Comfy Deploy logging enabled")
|
||||
setup()
|
||||
|
||||
try:
|
||||
# Get the absolute path of the script's directory
|
||||
|
||||
+238
-37
@@ -9,6 +9,76 @@ const ext = {
|
||||
|
||||
init(app) {
|
||||
addButton();
|
||||
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const workflow_version_id = queryParams.get("workflow_version_id");
|
||||
const auth_token = queryParams.get("auth_token");
|
||||
const org_display = queryParams.get("org_display");
|
||||
const origin = queryParams.get("origin");
|
||||
|
||||
const data = getData();
|
||||
let endpoint = data.endpoint;
|
||||
let apiKey = data.apiKey;
|
||||
|
||||
// If there is auth token override it
|
||||
if (auth_token) {
|
||||
apiKey = auth_token;
|
||||
endpoint = origin;
|
||||
saveData({
|
||||
displayName: org_display,
|
||||
endpoint: origin,
|
||||
apiKey: auth_token,
|
||||
displayName: org_display,
|
||||
environment: "cloud",
|
||||
});
|
||||
localStorage.setItem("comfy_deploy_env", "cloud");
|
||||
}
|
||||
|
||||
if (!workflow_version_id) {
|
||||
console.error("No workflow_version_id provided in query parameters.");
|
||||
} else {
|
||||
loadingDialog.showLoading(
|
||||
"Loading workflow from " + org_display,
|
||||
"Please wait...",
|
||||
);
|
||||
fetch(endpoint + "/api/workflow-version/" + workflow_version_id, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + apiKey,
|
||||
},
|
||||
})
|
||||
.then(async (res) => {
|
||||
const data = await res.json();
|
||||
const { workflow, workflow_id, error } = data;
|
||||
if (error) {
|
||||
infoDialog.showMessage("Unable to load this workflow", error);
|
||||
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} */
|
||||
app.loadGraphData(workflow);
|
||||
})
|
||||
.catch((e) => infoDialog.showMessage("Error", e.message))
|
||||
.finally(() => {
|
||||
loadingDialog.close();
|
||||
window.history.replaceState(
|
||||
{},
|
||||
document.title,
|
||||
window.location.pathname,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
registerCustomNodes() {
|
||||
@@ -120,7 +190,7 @@ function addButton() {
|
||||
const graph = app.graph;
|
||||
|
||||
const snapshot = await fetch("/snapshot/get_current").then((x) => x.json());
|
||||
console.log(snapshot);
|
||||
// console.log(snapshot);
|
||||
|
||||
if (!snapshot) {
|
||||
showError(
|
||||
@@ -154,7 +224,7 @@ function addButton() {
|
||||
|
||||
const deployMetaNode = deployMeta[0];
|
||||
|
||||
console.log(deployMetaNode);
|
||||
// console.log(deployMetaNode);
|
||||
|
||||
const workflow_name = deployMetaNode.widgets[0].value;
|
||||
const workflow_id = deployMetaNode.widgets[1].value;
|
||||
@@ -168,38 +238,43 @@ function addButton() {
|
||||
// const endpoint = localStorage.getItem("endpoint") ?? "";
|
||||
// const apiKey = localStorage.getItem("apiKey");
|
||||
|
||||
const { endpoint, apiKey } = getData();
|
||||
const { endpoint, apiKey, displayName } = getData();
|
||||
|
||||
if (!endpoint || !apiKey || apiKey === "" || endpoint === "") {
|
||||
configDialog.show();
|
||||
return;
|
||||
}
|
||||
|
||||
const ok = await confirmDialog.confirm("Confirm deployment", "A new version will be deployed, are you conform?")
|
||||
const ok = await confirmDialog.confirm(
|
||||
"Confirm deployment -> " + displayName,
|
||||
"A new version will be deployed, are you conform?",
|
||||
);
|
||||
if (!ok) return;
|
||||
|
||||
title.innerText = "Deploying...";
|
||||
title.style.color = "orange";
|
||||
|
||||
console.log(prompt);
|
||||
// console.log(prompt);
|
||||
|
||||
// TODO trim the ending / from endpoint is there is
|
||||
if (endpoint.endsWith("/")) {
|
||||
endpoint = endpoint.slice(0, -1);
|
||||
}
|
||||
|
||||
const apiRoute = endpoint + "/api/upload";
|
||||
const apiRoute = endpoint + "/api/workflow";
|
||||
// const userId = apiKey
|
||||
try {
|
||||
const body = {
|
||||
workflow_name,
|
||||
workflow_id,
|
||||
workflow: prompt.workflow,
|
||||
workflow_api: prompt.output,
|
||||
snapshot: snapshot,
|
||||
};
|
||||
console.log(body);
|
||||
let data = await fetch(apiRoute, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
workflow_name,
|
||||
workflow_id,
|
||||
workflow: prompt.workflow,
|
||||
workflow_api: prompt.output,
|
||||
snapshot: snapshot,
|
||||
}),
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + apiKey,
|
||||
@@ -275,14 +350,16 @@ export class InfoDialog extends ComfyDialog {
|
||||
this.element.classList.add("comfy-normal-modal");
|
||||
this.element.style.paddingBottom = "20px";
|
||||
}
|
||||
|
||||
button = undefined;
|
||||
|
||||
createButtons() {
|
||||
return [
|
||||
$el("button", {
|
||||
type: "button",
|
||||
textContent: "Close",
|
||||
onclick: () => this.close(),
|
||||
}),
|
||||
];
|
||||
this.button = $el("button", {
|
||||
type: "button",
|
||||
textContent: "Close",
|
||||
onclick: () => this.close(),
|
||||
});
|
||||
return [this.button];
|
||||
}
|
||||
|
||||
close() {
|
||||
@@ -301,6 +378,69 @@ export class InfoDialog extends ComfyDialog {
|
||||
this.element.style.display = "flex";
|
||||
this.element.style.zIndex = 1001;
|
||||
}
|
||||
|
||||
showMessage(title, message) {
|
||||
this.show(`
|
||||
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
||||
<h3 style="margin: 0px;">${title}</h3>
|
||||
<label>
|
||||
${message}
|
||||
</label>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
loadingIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="#888888" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg>`;
|
||||
|
||||
showLoading(title, message) {
|
||||
this.show(`
|
||||
<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;">${title} ${this.loadingIcon}</h3>
|
||||
<label>
|
||||
${message}
|
||||
</label>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
export class LoadingDialog extends ComfyDialog {
|
||||
constructor() {
|
||||
super();
|
||||
this.element.classList.add("comfy-normal-modal");
|
||||
// this.element.style.paddingBottom = "20px";
|
||||
}
|
||||
|
||||
createButtons() {
|
||||
return [];
|
||||
}
|
||||
|
||||
close() {
|
||||
this.element.style.display = "none";
|
||||
}
|
||||
|
||||
show(html) {
|
||||
this.textElement.style["white-space"] = "normal";
|
||||
this.textElement.style.color = "white";
|
||||
this.textElement.style.marginTop = "0px";
|
||||
if (typeof html === "string") {
|
||||
this.textElement.innerHTML = html;
|
||||
} else {
|
||||
this.textElement.replaceChildren(html);
|
||||
}
|
||||
this.element.style.display = "flex";
|
||||
this.element.style.zIndex = 1001;
|
||||
}
|
||||
|
||||
loadingIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="#888888" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg>`;
|
||||
|
||||
showLoading(title, message) {
|
||||
this.show(`
|
||||
<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>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
export class InputDialog extends InfoDialog {
|
||||
@@ -367,7 +507,6 @@ export class InputDialog extends InfoDialog {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ConfirmDialog extends InfoDialog {
|
||||
callback = undefined;
|
||||
|
||||
@@ -429,9 +568,15 @@ export class ConfirmDialog extends InfoDialog {
|
||||
}
|
||||
|
||||
export const inputDialog = new InputDialog();
|
||||
export const loadingDialog = new LoadingDialog();
|
||||
export const infoDialog = new InfoDialog();
|
||||
export const confirmDialog = new ConfirmDialog();
|
||||
|
||||
/**
|
||||
* Retrieves deployment data from local storage or defaults.
|
||||
* @param {string} [environment] - The environment to get the data for.
|
||||
* @returns {{endpoint: string, apiKey: string, displayName: string, environment?: string}} The deployment data.
|
||||
*/
|
||||
function getData(environment) {
|
||||
const deployOption =
|
||||
environment || localStorage.getItem("comfy_deploy_env") || "cloud";
|
||||
@@ -454,8 +599,21 @@ function getData(environment) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves deployment data from local storage or defaults.
|
||||
* @param {{endpoint: string, apiKey: string, displayName: string, environment?: string}} [data] - The environment to get the data for.
|
||||
*/
|
||||
function saveData(data) {
|
||||
localStorage.setItem(
|
||||
"comfy_deploy_env_data_" + data.environment,
|
||||
JSON.stringify(data),
|
||||
);
|
||||
}
|
||||
|
||||
export class ConfigDialog extends ComfyDialog {
|
||||
container = null;
|
||||
poll = null;
|
||||
timeout = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -498,22 +656,24 @@ export class ConfigDialog extends ComfyDialog {
|
||||
|
||||
close() {
|
||||
this.element.style.display = "none";
|
||||
clearInterval(this.poll);
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
save() {
|
||||
save(api_key, displayName) {
|
||||
if (!displayName) displayName = getData().displayName;
|
||||
|
||||
const deployOption = this.container.querySelector("#deployOption").value;
|
||||
localStorage.setItem("comfy_deploy_env", deployOption);
|
||||
|
||||
const endpoint = this.container.querySelector("#endpoint").value;
|
||||
const apiKey = this.container.querySelector("#apiKey").value;
|
||||
const data = {
|
||||
const apiKey = api_key ?? this.container.querySelector("#apiKey").value;
|
||||
saveData({
|
||||
endpoint,
|
||||
apiKey,
|
||||
};
|
||||
localStorage.setItem(
|
||||
"comfy_deploy_env_data_" + deployOption,
|
||||
JSON.stringify(data),
|
||||
);
|
||||
displayName,
|
||||
environment: deployOption,
|
||||
});
|
||||
this.close();
|
||||
}
|
||||
|
||||
@@ -527,12 +687,8 @@ export class ConfigDialog extends ComfyDialog {
|
||||
<h3 style="margin: 0px;">Comfy Deploy Config</h3>
|
||||
<label style="color: white; width: 100%;">
|
||||
<select id="deployOption" style="margin: 8px 0px; width: 100%; height:30px; box-sizing: border-box;" >
|
||||
<option value="cloud" ${
|
||||
data.environment === "cloud" ? "selected" : ""
|
||||
}>Cloud</option>
|
||||
<option value="local" ${
|
||||
data.environment === "local" ? "selected" : ""
|
||||
}>Local</option>
|
||||
<option value="cloud" ${data.environment === "cloud" ? "selected" : ""}>Cloud</option>
|
||||
<option value="local" ${data.environment === "local" ? "selected" : ""}>Local</option>
|
||||
</select>
|
||||
</label>
|
||||
<label style="color: white; width: 100%;">
|
||||
@@ -542,16 +698,61 @@ export class ConfigDialog extends ComfyDialog {
|
||||
}">
|
||||
</label>
|
||||
<label style="color: white;">
|
||||
API Key:
|
||||
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
|
||||
}">
|
||||
<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"
|
||||
}
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const button = this.container.querySelector("#loginButton");
|
||||
button.onclick = () => {
|
||||
const uuid =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
window.open(data.endpoint + "/auth-request/" + uuid, "_blank");
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
clearInterval(poll);
|
||||
infoDialog.showMessage(
|
||||
"Timeout",
|
||||
"Wait too long for the response, please try re-login",
|
||||
);
|
||||
}, 30000); // Stop polling after 30 seconds
|
||||
|
||||
this.poll = setInterval(() => {
|
||||
fetch(data.endpoint + "/api/auth-response/" + uuid)
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
if (json.api_key) {
|
||||
this.save(json.api_key, json.name);
|
||||
this.container.querySelector("#apiKey").value = json.api_key;
|
||||
infoDialog.show();
|
||||
clearInterval(this.poll);
|
||||
clearTimeout(this.timeout);
|
||||
infoDialog.showMessage(
|
||||
"Authenticated",
|
||||
"You will be able to upload workflow to " + json.name,
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
clearInterval(this.poll);
|
||||
clearTimeout(this.timeout);
|
||||
infoDialog.showMessage("Error", error);
|
||||
});
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const apiKeyInput = this.container.querySelector("#apiKey");
|
||||
apiKeyInput.addEventListener("paste", function (e) {
|
||||
apiKeyInput.addEventListener("paste", (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
|
||||
@@ -20,3 +20,14 @@ PLAUSIBLE_DOMAIN=
|
||||
|
||||
NEXT_PUBLIC_POSTHOG_KEY="your-api-key"
|
||||
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_"
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS "comfyui_deploy"."auth_requests" (
|
||||
"request_id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text,
|
||||
"org_id" text,
|
||||
"api_hash" text,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "comfyui_deploy"."auth_requests" ADD COLUMN "expired_date" timestamp;
|
||||
@@ -1,2 +0,0 @@
|
||||
ALTER TABLE "comfyui_deploy"."user_usage" RENAME COLUMN "updated_at" TO "ended_at";--> statement-breakpoint
|
||||
ALTER TABLE "comfyui_deploy"."user_usage" ADD COLUMN "org_id" text;
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
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,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
"ended_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
@@ -0,0 +1,22 @@
|
||||
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
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
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;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "comfyui_deploy"."subscription_status" ADD COLUMN "cancel_at_period_end" boolean DEFAULT false;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "user_id" text;--> statement-breakpoint
|
||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "org_id" text;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "gpu" "machine_gpu";
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "machine_type" "machine_type";
|
||||
@@ -0,0 +1,64 @@
|
||||
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 $$;
|
||||
@@ -0,0 +1,33 @@
|
||||
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 $$;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "comfyui_deploy"."models" ALTER COLUMN "is_public" SET DEFAULT true;
|
||||
@@ -0,0 +1,3 @@
|
||||
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;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TYPE "model_upload_type" ADD VALUE 'download_url';
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TYPE "model_upload_type" ADD VALUE 'download-url';
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TYPE "workflow_run_status" ADD VALUE 'timeout';--> statement-breakpoint
|
||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "run_log" text;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "comfyui_deploy"."workflow_runs" DROP COLUMN "run_log";
|
||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "run_log" jsonb;
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"id": "91bb0461-452a-4e59-abf4-8757fcd75a89",
|
||||
"id": "97662b25-3992-4859-9bdc-560e2a70daea",
|
||||
"prevId": "1425ee00-66fb-4541-8da7-19b217944545",
|
||||
"version": "5",
|
||||
"dialect": "pg",
|
||||
@@ -88,6 +88,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"deployments": {
|
||||
"name": "deployments",
|
||||
"schema": "comfyui_deploy",
|
||||
@@ -360,64 +408,6 @@
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"user_usage": {
|
||||
"name": "user_usage",
|
||||
"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
|
||||
},
|
||||
"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()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "fad17dc9-86c5-4081-8e73-47c113f48936",
|
||||
"prevId": "91bb0461-452a-4e59-abf4-8757fcd75a89",
|
||||
"id": "8d654f92-7f7e-420f-bbd3-73b6b27adf35",
|
||||
"prevId": "97662b25-3992-4859-9bdc-560e2a70daea",
|
||||
"version": "5",
|
||||
"dialect": "pg",
|
||||
"tables": {
|
||||
@@ -88,6 +88,60 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -360,70 +414,6 @@
|
||||
"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",
|
||||
@@ -835,8 +825,6 @@
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {
|
||||
"\"comfyui_deploy\".\"user_usage\".\"updated_at\"": "\"comfyui_deploy\".\"user_usage\".\"ended_at\""
|
||||
}
|
||||
"columns": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,894 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,970 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,984 @@
|
||||
{
|
||||
"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\""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,989 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -236,16 +236,121 @@
|
||||
{
|
||||
"idx": 33,
|
||||
"version": "5",
|
||||
"when": 1705824362978,
|
||||
"tag": "0033_fantastic_marvel_boy",
|
||||
"when": 1705853314500,
|
||||
"tag": "0033_awesome_human_fly",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 34,
|
||||
"version": "5",
|
||||
"when": 1705840184127,
|
||||
"tag": "0034_previous_viper",
|
||||
"when": 1705902960991,
|
||||
"tag": "0034_even_lady_ursula",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"idx": 48,
|
||||
"version": "5",
|
||||
"when": 1706600255919,
|
||||
"tag": "0048_dear_korath",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 49,
|
||||
"version": "5",
|
||||
"when": 1706631744127,
|
||||
"tag": "0049_sweet_hex",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+10
-6
@@ -14,8 +14,12 @@ if (sslMode === "false") sslMode = false;
|
||||
|
||||
let connectionString = process.env.POSTGRES_URL!;
|
||||
|
||||
const isDevContainer = process.env.VSCODE_DEV_CONTAINER !== undefined;
|
||||
if (isDevContainer) connectionString = connectionString.replace("localhost","host.docker.internal")
|
||||
const isDevContainer = process.env.REMOTE_CONTAINERS !== undefined;
|
||||
if (isDevContainer)
|
||||
connectionString = connectionString.replace(
|
||||
"localhost",
|
||||
"host.docker.internal",
|
||||
);
|
||||
|
||||
const sql = postgres(connectionString, { max: 1, ssl: sslMode as any });
|
||||
const db = drizzle(sql, {
|
||||
@@ -23,16 +27,16 @@ const db = drizzle(sql, {
|
||||
});
|
||||
|
||||
let retries = 5;
|
||||
while(retries) {
|
||||
while (retries) {
|
||||
try {
|
||||
await sql`SELECT NOW()`;
|
||||
console.log('Database is live');
|
||||
console.log("Database is live");
|
||||
break;
|
||||
} catch (error) {
|
||||
console.error('Database is not live yet', error);
|
||||
console.error("Database is not live yet", error);
|
||||
retries -= 1;
|
||||
console.log(`Retries left: ${retries}`);
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
await new Promise((res) => setTimeout(res, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -19,7 +19,7 @@
|
||||
"@algolia/autocomplete-core": "^1.13.0",
|
||||
"@aws-sdk/client-s3": "^3.472.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.472.0",
|
||||
"@clerk/nextjs": "^4.29.3",
|
||||
"@clerk/nextjs": "4.29.5",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@hono/swagger-ui": "^0.2.1",
|
||||
@@ -32,11 +32,13 @@
|
||||
"@neondatabase/serverless": "^0.6.0",
|
||||
"@next/mdx": "^14.0.4",
|
||||
"@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-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@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-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
@@ -47,6 +49,7 @@
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"@stripe/stripe-js": "^2.4.0",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@tanstack/react-table": "^8.10.7",
|
||||
"@tanstack/react-virtual": "beta",
|
||||
@@ -73,6 +76,7 @@
|
||||
"mdx-annotations": "^0.1.4",
|
||||
"million": "latest",
|
||||
"mitata": "^0.1.6",
|
||||
"ms": "^2.1.3",
|
||||
"nanoid": "^5.0.4",
|
||||
"next": "14.1",
|
||||
"next-plausible": "^3.12.0",
|
||||
@@ -94,6 +98,7 @@
|
||||
"shikiji": "^0.9.3",
|
||||
"simple-functional-loader": "^1.2.1",
|
||||
"sonner": "^1.2.4",
|
||||
"stripe": "^14.13.0",
|
||||
"swagger-ui-react": "^5.11.0",
|
||||
"swr": "^2.2.4",
|
||||
"tailwind-merge": "^2.1.0",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { app } from "../../../../routes/app";
|
||||
import { registerCreateRunRoute } from "@/routes/registerCreateRunRoute";
|
||||
import { registerGetOutputRoute } from "@/routes/registerGetOutputRoute";
|
||||
import { registerUploadRoute } from "@/routes/registerUploadRoute";
|
||||
@@ -6,7 +5,11 @@ import { isKeyRevoked } from "@/server/curdApiKeys";
|
||||
import { parseJWT } from "@/server/parseJWT";
|
||||
import type { Context, Next } from "hono";
|
||||
import { handle } from "hono/vercel";
|
||||
|
||||
import { app } from "../../../../routes/app";
|
||||
import { registerWorkflowUploadRoute } from "@/routes/registerWorkflowUploadRoute";
|
||||
import { registerGetAuthResponse } from "@/routes/registerGetAuthResponse";
|
||||
import { registerGetWorkflowRoute } from "@/routes/registerGetWorkflow";
|
||||
import { cors } from "hono/cors";
|
||||
export const dynamic = "force-dynamic";
|
||||
export const maxDuration = 300; // 5 minutes
|
||||
|
||||
@@ -16,14 +19,24 @@ declare module "hono" {
|
||||
}
|
||||
}
|
||||
|
||||
async function checkAuth(c: Context, next: Next) {
|
||||
async function checkAuth(c: Context, next: Next, headers?: HeadersInit) {
|
||||
const token = c.req.raw.headers.get("Authorization")?.split(" ")?.[1]; // Assuming token is sent as "Bearer your_token"
|
||||
const userData = token ? parseJWT(token) : undefined;
|
||||
if (!userData || token === undefined) {
|
||||
return c.text("Invalid or expired token", 401);
|
||||
} else {
|
||||
return c.text("Invalid or expired token", {
|
||||
status: 401,
|
||||
headers: headers,
|
||||
});
|
||||
}
|
||||
|
||||
// If the key has expiration, this is a temporary key and not in our db, so we can skip checking
|
||||
if (userData.exp === undefined) {
|
||||
const revokedKey = await isKeyRevoked(token);
|
||||
if (revokedKey) return c.text("Revoked token", 401);
|
||||
if (revokedKey)
|
||||
return c.text("Revoked token", {
|
||||
status: 401,
|
||||
headers: headers,
|
||||
});
|
||||
}
|
||||
|
||||
c.set("apiKeyTokenData", userData);
|
||||
@@ -31,18 +44,35 @@ async function checkAuth(c: Context, next: Next) {
|
||||
await next();
|
||||
}
|
||||
|
||||
app.use("/run", async (c, next) => {
|
||||
return checkAuth(c, next);
|
||||
app.use("/run", checkAuth);
|
||||
app.use("/upload-url", checkAuth);
|
||||
|
||||
const corsHandler = cors({
|
||||
origin: "*",
|
||||
allowHeaders: ["Authorization", "Content-Type"],
|
||||
allowMethods: ["POST", "GET", "OPTIONS"],
|
||||
exposeHeaders: ["Content-Length"],
|
||||
maxAge: 600,
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
app.use("/upload-url", async (c, next) => {
|
||||
return checkAuth(c, next);
|
||||
});
|
||||
// CORS Check
|
||||
app.use("/workflow", corsHandler, checkAuth);
|
||||
app.use("/workflow-version/*", corsHandler, checkAuth);
|
||||
|
||||
// create run endpoint
|
||||
registerCreateRunRoute(app);
|
||||
registerGetOutputRoute(app);
|
||||
|
||||
// file upload endpoint
|
||||
registerUploadRoute(app);
|
||||
|
||||
// Anon
|
||||
registerGetAuthResponse(app);
|
||||
|
||||
registerWorkflowUploadRoute(app);
|
||||
registerGetWorkflowRoute(app);
|
||||
|
||||
// The OpenAPI documentation will be available at /doc
|
||||
app.doc("/doc", {
|
||||
openapi: "3.0.0",
|
||||
@@ -69,3 +99,4 @@ const handler = handle(app);
|
||||
|
||||
export const GET = handler;
|
||||
export const POST = handler;
|
||||
export const OPTIONS = handler;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||
import { handleResourceUpload } from "@/server/resource";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||
|
||||
const Request = z.object({
|
||||
file_name: z.string(),
|
||||
run_id: z.string(),
|
||||
|
||||
type: z.string(),
|
||||
});
|
||||
|
||||
@@ -29,7 +30,7 @@ export async function GET(request: Request) {
|
||||
{
|
||||
url: uploadUrl,
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const errorMessage =
|
||||
@@ -38,7 +39,7 @@ export async function GET(request: Request) {
|
||||
{
|
||||
error: errorMessage,
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
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 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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,113 +1,150 @@
|
||||
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||
import { db } from "@/db/db";
|
||||
import {
|
||||
userUsageTable,
|
||||
WorkflowRunStatusSchema,
|
||||
workflowRunOutputs,
|
||||
workflowRunsTable,
|
||||
workflowTable,
|
||||
} from "@/db/schema";
|
||||
import { getDuration } from "@/lib/getRelativeTime";
|
||||
import { getSubscription, setUsage } from "@/server/linkToPricing";
|
||||
import { getCurrentPlan } from "@/server/getCurrentPlan";
|
||||
import { stripe } from "@/server/stripe";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
const Request = z.object({
|
||||
run_id: z.string(),
|
||||
status: z
|
||||
.enum(["not-started", "running", "uploading", "success", "failed"])
|
||||
.optional(),
|
||||
status: WorkflowRunStatusSchema.optional(),
|
||||
time: z.coerce.date().optional(),
|
||||
output_data: z.any().optional(),
|
||||
log_data: z.any().optional(),
|
||||
});
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const [data, error] = await parseDataSafe(Request, request);
|
||||
if (!data || error) return error;
|
||||
try {
|
||||
const [data, error] = await parseDataSafe(Request, request);
|
||||
|
||||
const { run_id, status, output_data } = data;
|
||||
if (!data || error) return error;
|
||||
|
||||
// console.log(run_id, status, output_data);
|
||||
const { run_id, status, time, output_data, log_data } = data;
|
||||
|
||||
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));
|
||||
if (log_data) {
|
||||
// It successfully started, update the started_at time
|
||||
await db
|
||||
.update(workflowRunsTable)
|
||||
.set({
|
||||
run_log: log_data,
|
||||
})
|
||||
.where(eq(workflowRunsTable.id, run_id));
|
||||
|
||||
// get data from workflowRunsTable
|
||||
const userUsageTime = await importUserUsageData(run_id);
|
||||
|
||||
if (userUsageTime) {
|
||||
// get the usage_time from userUsage
|
||||
await addSubscriptionUnit(userUsageTime);
|
||||
return NextResponse.json(
|
||||
{
|
||||
message: "success",
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
if (status == "started" && time != undefined) {
|
||||
// It successfully started, update the started_at time
|
||||
await db
|
||||
.update(workflowRunsTable)
|
||||
.set({
|
||||
started_at: time,
|
||||
})
|
||||
.where(eq(workflowRunsTable.id, run_id));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function addSubscriptionUnit(userUsageTime: number) {
|
||||
const subscription = await getSubscription();
|
||||
if (status == "queued" && time != undefined) {
|
||||
// It successfully started, update the started_at time
|
||||
await db
|
||||
.update(workflowRunsTable)
|
||||
.set({
|
||||
queued_at: time,
|
||||
})
|
||||
.where(eq(workflowRunsTable.id, run_id));
|
||||
}
|
||||
|
||||
// round up userUsageTime to the nearest integer
|
||||
const roundedUsageTime = Math.ceil(userUsageTime);
|
||||
const ended =
|
||||
status === "success" || status === "failed" || status === "timeout";
|
||||
|
||||
if (subscription) {
|
||||
const usage = await setUsage(
|
||||
subscription.data[0].attributes.first_subscription_item.id,
|
||||
roundedUsageTime
|
||||
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: ended ? 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 (ended && 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;
|
||||
}
|
||||
try {
|
||||
await stripe.subscriptionItems.createUsageRecord(
|
||||
sub.subscription_item_api_id,
|
||||
{
|
||||
quantity: durationInSec,
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function importUserUsageData(run_id: string) {
|
||||
const workflowRuns = await db.query.workflowRunsTable.findFirst({
|
||||
where: eq(workflowRunsTable.id, run_id),
|
||||
});
|
||||
|
||||
if (!workflowRuns?.workflow_id) return;
|
||||
|
||||
// find if workflowTable id column contains workflowRunsTable workflow_id
|
||||
const workflow = await db.query.workflowTable.findFirst({
|
||||
where: eq(workflowTable.id, workflowRuns.workflow_id),
|
||||
});
|
||||
|
||||
if (workflowRuns?.ended_at === null || workflow == null) return;
|
||||
|
||||
const usageTime = parseFloat(
|
||||
getDuration((workflowRuns?.ended_at - workflowRuns?.started_at) / 1000)
|
||||
);
|
||||
|
||||
// add data to userUsageTable
|
||||
const user_usage = await db.insert(userUsageTable).values({
|
||||
user_id: workflow.user_id,
|
||||
created_at: workflowRuns.ended_at,
|
||||
org_id: workflow.org_id,
|
||||
ended_at: workflowRuns.ended_at,
|
||||
usage_time: usageTime,
|
||||
});
|
||||
|
||||
return usageTime;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { createNewWorkflow } from "../../../../server/createNewWorkflow";
|
||||
import { parseJWT } from "../../../../server/parseJWT";
|
||||
import { db } from "@/db/db";
|
||||
import {
|
||||
snapshotType,
|
||||
workflowAPIType,
|
||||
workflowTable,
|
||||
workflowType,
|
||||
workflowVersionTable,
|
||||
} from "@/db/schema";
|
||||
import { snapshotType, workflowAPIType, workflowType } from "@/db/schema";
|
||||
import { parseDataSafe } from "@/lib/parseDataSafe";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
createNewWorkflow,
|
||||
createNewWorkflowVersion,
|
||||
} from "../../../../server/createNewWorkflow";
|
||||
import { parseJWT } from "../../../../server/parseJWT";
|
||||
|
||||
// This is will be deprecated
|
||||
|
||||
const corsHeaders = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
@@ -55,7 +52,7 @@ export async function POST(request: Request) {
|
||||
const [data, error] = await parseDataSafe(
|
||||
UploadRequest,
|
||||
request,
|
||||
corsHeaders
|
||||
corsHeaders,
|
||||
);
|
||||
|
||||
if (!data || error) return error;
|
||||
@@ -75,7 +72,7 @@ export async function POST(request: Request) {
|
||||
|
||||
// Case 1 new workflow
|
||||
try {
|
||||
if ((!workflow_id || workflow_id.length == 0) && workflow_name) {
|
||||
if ((!workflow_id || workflow_id.length === 0) && workflow_name) {
|
||||
// Create a new parent workflow
|
||||
const { workflow_id: _workflow_id, version: _version } =
|
||||
await createNewWorkflow({
|
||||
@@ -91,56 +88,17 @@ export async function POST(request: Request) {
|
||||
|
||||
workflow_id = _workflow_id;
|
||||
version = _version;
|
||||
|
||||
// const workflow_parent = await db
|
||||
// .insert(workflowTable)
|
||||
// .values({
|
||||
// user_id,
|
||||
// name: workflow_name,
|
||||
// org_id: org_id,
|
||||
// })
|
||||
// .returning();
|
||||
|
||||
// workflow_id = workflow_parent[0].id;
|
||||
|
||||
// // Create a new version
|
||||
// const data = await db
|
||||
// .insert(workflowVersionTable)
|
||||
// .values({
|
||||
// workflow_id: workflow_id,
|
||||
// workflow,
|
||||
// workflow_api,
|
||||
// version: 1,
|
||||
// snapshot: snapshot,
|
||||
// })
|
||||
// .returning();
|
||||
// version = data[0].version;
|
||||
} else if (workflow_id) {
|
||||
// Case 2 update workflow
|
||||
const data = await db
|
||||
.insert(workflowVersionTable)
|
||||
.values({
|
||||
workflow_id,
|
||||
workflow: workflow,
|
||||
const { version: _version } = await createNewWorkflowVersion({
|
||||
workflow_id: workflow_id,
|
||||
workflowData: {
|
||||
workflow,
|
||||
workflow_api,
|
||||
// version: sql`${workflowVersionTable.version} + 1`,
|
||||
snapshot: snapshot,
|
||||
version: sql`(
|
||||
SELECT COALESCE(MAX(version), 0) + 1
|
||||
FROM ${workflowVersionTable}
|
||||
WHERE workflow_id = ${workflow_id}
|
||||
)`,
|
||||
})
|
||||
.returning();
|
||||
version = data[0].version;
|
||||
|
||||
await db
|
||||
.update(workflowTable)
|
||||
.set({
|
||||
updated_at: new Date(),
|
||||
})
|
||||
.where(eq(workflowTable.id, workflow_id))
|
||||
.returning();
|
||||
snapshot,
|
||||
},
|
||||
});
|
||||
version = _version;
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{
|
||||
@@ -150,7 +108,7 @@ export async function POST(request: Request) {
|
||||
status: 500,
|
||||
statusText: "Invalid request",
|
||||
headers: corsHeaders,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -162,7 +120,7 @@ export async function POST(request: Request) {
|
||||
status: 500,
|
||||
statusText: "Invalid request",
|
||||
headers: corsHeaders,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -174,6 +132,6 @@ export async function POST(request: Request) {
|
||||
{
|
||||
status: 200,
|
||||
headers: corsHeaders,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
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,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ButtonAction } from "@/components/ButtonActionLoader";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { createAuthRequest } from "@/server/curdApiKeys";
|
||||
import { auth } from "@clerk/nextjs";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getOrgOrUserDisplayName } from "../../../../server/getOrgOrUserDisplayName";
|
||||
import { db } from "@/db/db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { authRequestsTable } from "@/db/schema";
|
||||
|
||||
export default async function Home({
|
||||
params,
|
||||
}: {
|
||||
params: { request_id: string };
|
||||
}) {
|
||||
const { userId, orgId } = await auth();
|
||||
|
||||
if (!userId) redirect("/");
|
||||
|
||||
if (!params.request_id)
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col gap-2 items-center justify-center">
|
||||
No valid request_id
|
||||
</div>
|
||||
);
|
||||
|
||||
const existingResult = await db.query.authRequestsTable.findFirst({
|
||||
where: eq(authRequestsTable.request_id, params.request_id),
|
||||
});
|
||||
|
||||
if (existingResult?.api_hash) {
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col gap-2 items-center justify-center">
|
||||
Request already consumed.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const userName = await getOrgOrUserDisplayName(orgId, userId);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col gap-2 items-center justify-center">
|
||||
<div className="text-lg">Grant API Access to {userName}</div>
|
||||
<Button asChild>
|
||||
<ButtonAction
|
||||
routerAction="do-nothing"
|
||||
action={createAuthRequest.bind(null, params.request_id)}
|
||||
>
|
||||
Grant Access
|
||||
</ButtonAction>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { SignIn } from "@clerk/nextjs";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="w-full h-full items-center flex justify-center">
|
||||
<SignIn />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { SignUp } from "@clerk/nextjs";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="w-full h-full items-center flex justify-center">
|
||||
<SignUp />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -35,7 +35,10 @@ export default async function Page({
|
||||
/>
|
||||
)}
|
||||
{machine.status !== "building" && machine.build_log && (
|
||||
<LogsViewer logs={JSON.parse(machine.build_log)} />
|
||||
<LogsViewer
|
||||
logs={JSON.parse(machine.build_log)}
|
||||
className="h-full max-h-[600px]"
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { AccessType } from "../../../lib/AccessType";
|
||||
import { MachineList } from "@/components/MachineList";
|
||||
import { SubscriptionProvider } from "@/components/useCurrentPlan";
|
||||
import { db } from "@/db/db";
|
||||
import { machinesTable } from "@/db/schema";
|
||||
import { getCurrentPlanWithAuth } from "@/server/getCurrentPlan";
|
||||
import { auth } from "@clerk/nextjs";
|
||||
import { clerkClient } from "@clerk/nextjs/server";
|
||||
import { desc, eq, isNull, and } from "drizzle-orm";
|
||||
@@ -19,6 +21,8 @@ async function MachineListServer() {
|
||||
|
||||
const user = await clerkClient.users.getUser(userId);
|
||||
|
||||
const sub = await getCurrentPlanWithAuth();
|
||||
|
||||
const machines = await db.query.machinesTable.findMany({
|
||||
orderBy: desc(machinesTable.updated_at),
|
||||
where:
|
||||
@@ -29,11 +33,12 @@ async function MachineListServer() {
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{/* <div>Machines</div> */}
|
||||
<MachineList
|
||||
data={machines}
|
||||
userMetadata={AccessType.parse(user.privateMetadata ?? {})}
|
||||
/>
|
||||
<SubscriptionProvider sub={sub}>
|
||||
<MachineList
|
||||
data={machines}
|
||||
userMetadata={AccessType.parse(user.privateMetadata ?? {})}
|
||||
/>
|
||||
</SubscriptionProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
const people = [
|
||||
{
|
||||
name: "Nvidia T4 GPU",
|
||||
gpu: "1x",
|
||||
ram: "16GB",
|
||||
price: "$0.000225/sec",
|
||||
},
|
||||
{
|
||||
name: "Nvidia A40 GPU",
|
||||
gpu: "1x",
|
||||
ram: "48GB",
|
||||
price: "$0.000575/sec",
|
||||
},
|
||||
];
|
||||
|
||||
export function GpuPricingPlan() {
|
||||
return (
|
||||
<div className="flex justify-center w-full py-8">
|
||||
<div className="w-full max-w-4xl">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6"
|
||||
>
|
||||
GPU
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell"
|
||||
>
|
||||
No.
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:table-cell"
|
||||
>
|
||||
RAM
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Price
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 bg-white">
|
||||
{people.map((person) => (
|
||||
<tr key={person.ram} className="even:bg-gray-50">
|
||||
<td className="w-full max-w-0 py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:w-auto sm:max-w-none sm:pl-6">
|
||||
{person.name}
|
||||
<dl className="font-normal lg:hidden">
|
||||
<dt className="sr-only">No.</dt>
|
||||
<dd className="mt-1 truncate text-gray-700">
|
||||
{person.gpu}
|
||||
</dd>
|
||||
<dt className="sr-only sm:hidden">RAM</dt>
|
||||
<dd className="mt-1 truncate text-gray-500 sm:hidden">
|
||||
{person.ram}
|
||||
</dd>
|
||||
</dl>
|
||||
</td>
|
||||
<td className="hidden px-3 py-4 text-sm text-gray-500 lg:table-cell">
|
||||
{person.gpu}
|
||||
</td>
|
||||
<td className="hidden px-3 py-4 text-sm text-gray-500 sm:table-cell">
|
||||
{person.ram}
|
||||
</td>
|
||||
<td className="px-3 py-4 text-sm text-gray-500">
|
||||
{person.price}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
import { checkMarkIcon, crossMarkIcon } from "../const/Icon";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getPricing } from "@/server/linkToPricing";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type Tier = {
|
||||
name: string;
|
||||
id: string;
|
||||
href: string;
|
||||
priceMonthly: string;
|
||||
description: string;
|
||||
features: string[];
|
||||
featured: boolean;
|
||||
priority?: TierPriority;
|
||||
};
|
||||
|
||||
enum TierPriority {
|
||||
Free = "free",
|
||||
Pro = "pro",
|
||||
Enterprise = "enterprise",
|
||||
}
|
||||
|
||||
export default function PricingList() {
|
||||
const [productTiers, setProductTiers] = useState<Tier[]>();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const product = await getPricing();
|
||||
|
||||
if (!product) return;
|
||||
|
||||
const newProductTiers: Tier[] = product.data.map((item) => {
|
||||
// Create a new DOMParser instance
|
||||
const parser = new DOMParser();
|
||||
// Parse the description HTML string to a new document
|
||||
const doc = parser.parseFromString(
|
||||
item.attributes.description,
|
||||
"text/html"
|
||||
);
|
||||
// Extract the description and features
|
||||
const description = doc.querySelector("p")?.textContent || "";
|
||||
const features = Array.from(doc.querySelectorAll("ul > li")).map(
|
||||
(li) => li.textContent || ""
|
||||
);
|
||||
|
||||
return {
|
||||
name: item.attributes.name,
|
||||
id: item.id,
|
||||
href: item.attributes.buy_now_url,
|
||||
priceMonthly:
|
||||
item.attributes.price_formatted.split("/")[0] == "Usage-based"
|
||||
? "$20.00"
|
||||
: item.attributes.price_formatted.split("/")[0],
|
||||
description: description,
|
||||
features: features,
|
||||
|
||||
// if name contains pro, it's featured
|
||||
featured: item.attributes.name.toLowerCase().includes("pro"),
|
||||
|
||||
// give priority if name contain in enum
|
||||
priority: Object.values(TierPriority).find((priority) =>
|
||||
item.attributes.name.toLowerCase().includes(priority)
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
// sort newProductTiers by priority
|
||||
newProductTiers.sort((a, b) => {
|
||||
if (!a.priority) return 1;
|
||||
if (!b.priority) return -1;
|
||||
return (
|
||||
Object.values(TierPriority).indexOf(a.priority) -
|
||||
Object.values(TierPriority).indexOf(b.priority)
|
||||
);
|
||||
});
|
||||
|
||||
setProductTiers(newProductTiers);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="relative isolate px-6 py-24 lg:px-8">
|
||||
<div className="mx-auto max-w-2xl text-center lg:max-w-4xl">
|
||||
<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">
|
||||
The right price for you, whoever you are
|
||||
</p>
|
||||
</div>
|
||||
<p className="mx-auto mt-6 max-w-2xl text-center text-lg leading-8 text-gray-600">
|
||||
Qui iusto aut est earum eos quae. Eligendi est at nam aliquid ad quo
|
||||
reprehenderit in aliquid fugiat dolorum voluptatibus.
|
||||
</p>
|
||||
<div className="mx-auto mt-16 grid max-w-lg grid-cols-1 items-center gap-y-6 sm:mt-20 sm:gap-y-0 lg:max-w-4xl lg:grid-cols-2 xl:max-w-6xl xl:grid-cols-3">
|
||||
{productTiers &&
|
||||
productTiers.map((tier, tierIdx) => (
|
||||
<div
|
||||
key={tier.id}
|
||||
className={cn(
|
||||
tier.featured
|
||||
? "relative bg-white shadow-2xl"
|
||||
: "bg-white/60 sm:mx-8 lg:mx-0",
|
||||
tier.featured
|
||||
? ""
|
||||
: tierIdx === 0
|
||||
? "rounded-t-3xl sm:rounded-b-none lg:rounded-tr-none lg:rounded-bl-3xl"
|
||||
: "sm:rounded-t-none lg:rounded-tr-3xl lg:rounded-bl-none",
|
||||
"rounded-3xl p-8 ring-1 ring-gray-900/10 sm:p-10"
|
||||
)}
|
||||
>
|
||||
<h3
|
||||
id={tier.id}
|
||||
className="text-base font-semibold leading-7 text-indigo-600"
|
||||
>
|
||||
{tier.name}
|
||||
</h3>
|
||||
<p className="mt-4 flex items-baseline gap-x-2">
|
||||
<span className="text-5xl font-bold tracking-tight text-gray-900">
|
||||
{tier.priceMonthly}
|
||||
</span>
|
||||
<span className="text-base text-gray-500">/month</span>
|
||||
</p>
|
||||
<p className="mt-6 text-base leading-7 text-gray-600">
|
||||
{tier.description}
|
||||
</p>
|
||||
<ul
|
||||
role="list"
|
||||
className="mt-8 space-y-3 text-sm leading-6 text-gray-600 sm:mt-10"
|
||||
>
|
||||
{tier.features.map((feature) => (
|
||||
<li key={feature} className="flex gap-x-3">
|
||||
<div className="flex justify-center items-center">
|
||||
{feature.includes("[x]") ? crossMarkIcon : checkMarkIcon}
|
||||
</div>
|
||||
{feature.replace("[x]", "")}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<a
|
||||
href={tier.href}
|
||||
aria-describedby={tier.id}
|
||||
className={cn(
|
||||
tier.featured
|
||||
? "bg-indigo-600 text-white shadow 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.5 px-3.5 text-center text-sm font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 sm:mt-10"
|
||||
)}
|
||||
>
|
||||
Get started today
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
export const checkMarkIcon = (
|
||||
<svg
|
||||
className="h-5 w-5 flex-shrink-0 text-green-500"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const crossMarkIcon = (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 48 48"
|
||||
>
|
||||
<path
|
||||
fill="#F44336"
|
||||
d="M21.5 4.5H26.501V43.5H21.5z"
|
||||
transform="rotate(45.001 24 24)"
|
||||
/>
|
||||
<path
|
||||
fill="#F44336"
|
||||
d="M21.5 4.5H26.5V43.501H21.5z"
|
||||
transform="rotate(135.008 24 24)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,13 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { GpuPricingPlan } from "@/app/(app)/pricing/components/gpuPricingTable";
|
||||
import PricingList from "@/app/(app)/pricing/components/pricePlanList";
|
||||
import PricingList from "@/components/PricingPlan";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
<PricingList />
|
||||
<GpuPricingPlan />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
// app/providers.tsx
|
||||
'use client'
|
||||
import posthog from 'posthog-js'
|
||||
import { PostHogProvider } from 'posthog-js/react'
|
||||
"use client";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider } from "posthog-js/react";
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window !== "undefined") {
|
||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
||||
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({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <PostHogProvider client={posthog}>{children}</PostHogProvider>
|
||||
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { LoadingPageWrapper } from "@/components/LoadingWrapper";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
export default function Loading() {
|
||||
const pathName = usePathname();
|
||||
return <LoadingPageWrapper className="h-full" tag={pathName.toLowerCase()} />;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { LoadingPageWrapper } from "@/components/LoadingWrapper";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
export default function Loading() {
|
||||
const pathName = usePathname();
|
||||
return <LoadingPageWrapper className="h-full" tag={pathName.toLowerCase()} />;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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();
|
||||
|
||||
let data: Awaited<
|
||||
ReturnType<typeof stripe.subscriptionItems.listUsageRecordSummaries>
|
||||
> | null = null;
|
||||
|
||||
try {
|
||||
data = sub?.subscription_item_api_id
|
||||
? await stripe.subscriptionItems.listUsageRecordSummaries(
|
||||
sub?.subscription_item_api_id,
|
||||
)
|
||||
: null;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LoadingWrapper } from "@/components/LoadingWrapper";
|
||||
import { DeploymentsTable } from "@/components/RunsTable";
|
||||
import { DeploymentsTable } from "@/components/DeploymentsTable";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
export default async function Page({
|
||||
|
||||
@@ -13,12 +13,12 @@ export default async function Page({
|
||||
const workflow_id = params.workflow_id;
|
||||
|
||||
return (
|
||||
<Card className="w-full h-fit min-w-0">
|
||||
<CardHeader className="relative">
|
||||
<Card className="w-full h-fit min-w-0 relative">
|
||||
<CardHeader>
|
||||
<CardTitle>Run</CardTitle>
|
||||
<div className="absolute right-6 top-6">
|
||||
{/* <div className="absolute right-6 top-6">
|
||||
<RouteRefresher interval={5000} autoRefresh={false} />
|
||||
</div>
|
||||
</div> */}
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
|
||||
@@ -2,13 +2,14 @@ import { CreateShareButton } from "@/components/CreateShareButton";
|
||||
import { MachinesWSMain } from "@/components/MachinesWS";
|
||||
import { VersionDetails } from "@/components/VersionDetails";
|
||||
import {
|
||||
CopyWorkflowVersion,
|
||||
CreateDeploymentButton,
|
||||
MachineSelect,
|
||||
RunWorkflowButton,
|
||||
VersionSelect,
|
||||
ViewWorkflowDetailsButton,
|
||||
CopyWorkflowVersion,
|
||||
CreateDeploymentButton,
|
||||
MachineSelect,
|
||||
OpenEditButton,
|
||||
RunWorkflowButton,
|
||||
VersionSelect,
|
||||
} from "@/components/VersionSelect";
|
||||
import { ViewWorkflowDetailsButton } from "@/components/ViewWorkflowDetailsButton";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -20,6 +21,8 @@ import { getRelativeTime } from "@/lib/getRelativeTime";
|
||||
import { getMachines } from "@/server/curdMachine";
|
||||
import { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
||||
|
||||
export const maxDuration = 300; // 5 minutes
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
@@ -48,6 +51,7 @@ export default async function Page({
|
||||
<CreateShareButton workflow={workflow} machines={machines} />
|
||||
<CopyWorkflowVersion workflow={workflow} />
|
||||
<ViewWorkflowDetailsButton workflow={workflow} />
|
||||
<OpenEditButton workflow={workflow} machines={machines} />
|
||||
</div>
|
||||
|
||||
<VersionDetails workflow={workflow} />
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const maxDuration = 300; // 5 minutes
|
||||
|
||||
export default async function Layout({
|
||||
children,
|
||||
deployment,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { setInitialUserData } from "../../../lib/setInitialUserData";
|
||||
import { getAllUserWorkflow } from "../../../server/getAllUserWorkflow";
|
||||
import { getAllUserWorkflow } from "../../../server/crudWorkflow";
|
||||
import { WorkflowList } from "@/components/WorkflowList";
|
||||
import { db } from "@/db/db";
|
||||
import { usersTable } from "@/db/schema";
|
||||
import { auth } from "@clerk/nextjs";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export const maxDuration = 300; // 5 minutes
|
||||
|
||||
export default function Home() {
|
||||
return <WorkflowServer />;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
export const metadata = {
|
||||
title: 'Quickstart',
|
||||
description:
|
||||
'This guide will get you all set up and ready to use the Protocol API. We’ll cover how to get started an API client and how to make your first API request.',
|
||||
'This guide will get you all set up and ready to use Comfy Deploy. We’ll cover how to get started an API client and how to make your first API request.',
|
||||
}
|
||||
|
||||
# Getting stated
|
||||
# Getting started
|
||||
|
||||
Install Comfy Deploy's plugin on your local machine to get started with deploying workflow.
|
||||
Install Comfy Deploy's plugin on your local ComfyUI to get started with deploying workflow.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
export const metadata = {
|
||||
title: 'Comfy Deploy Video tutorials',
|
||||
description:
|
||||
'Get started with API integration to run any deploy ComfyUI workflow',
|
||||
}
|
||||
|
||||
**This is a collection of video tutorials on questions that have come up in our [discord channel](https://discord.gg/Zrar7yP6MJ).**
|
||||
|
||||
# Comfy Deploy workflow walkthrough (comprehensive)
|
||||
|
||||
Local workflow, to production API
|
||||
- Set inputs with Comfy Deploy `External Text` nodes
|
||||
- Deploy the workflow
|
||||
- Create a machine
|
||||
- Deploy an API endpoint for the workflow
|
||||
- Test the workflow in Comfy Deploy
|
||||
|
||||
<video controls>
|
||||
<source src="https://pub-1a2bca0642c24fcfb84ce8d8415958d3.r2.dev/comfydeploy_base-sd-setup.mp4" type="video/mp4"/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
|
||||
|
||||
# Test with Docs
|
||||
Shows how you can use the <a href='/docs/endpoints'>endpoints docs</a> to test out the API!
|
||||
|
||||
<video controls>
|
||||
<source src="https://pub-1a2bca0642c24fcfb84ce8d8415958d3.r2.dev/comfydeploy_interactive_docs.mp4" type="video/mp4"/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
## Intermediate IPAdaptor example run
|
||||
Shows off a semi-complex workflow working on Comfy Deploy
|
||||
|
||||
<video controls>
|
||||
<source src="https://pub-1a2bca0642c24fcfb84ce8d8415958d3.r2.dev/ipadapters-test.mp4" type="video/mp4"/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
## Animate diff on Comfy Deploy
|
||||
Shows off animate diff working on Comfy Deploy
|
||||
|
||||
<video controls>
|
||||
<source src="https://pub-1a2bca0642c24fcfb84ce8d8415958d3.r2.dev/comfydeploy-animediff.mp4" type="video/mp4"/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
## Cloning workflows
|
||||
What does cloning a workflow look like
|
||||
|
||||
<video controls>
|
||||
<source src="https://pub-1a2bca0642c24fcfb84ce8d8415958d3.r2.dev/comfydeploy-clone-template.mp4" type="video/mp4"/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
## [Install custom nodes from any github repo (Loom)](https://www.loom.com/share/c2025c5060e348839ade6a3a9d96441d?sid=6a57f779-0209-4341-a5fd-f99769cdb162)
|
||||
- This is only neccessary if the custom node is not in the ComfyUI Manager node list
|
||||
@@ -4,10 +4,10 @@ import { LoadingIcon } from "@/components/LoadingIcon";
|
||||
import { callServerPromise } from "@/components/callServerPromise";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { useAuth, useClerk } from "@clerk/nextjs";
|
||||
import { MoreVertical } from "lucide-react";
|
||||
@@ -15,80 +15,79 @@ import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
export function ButtonAction({
|
||||
action,
|
||||
children,
|
||||
routerAction = "back",
|
||||
...rest
|
||||
action,
|
||||
children,
|
||||
routerAction = "back",
|
||||
...rest
|
||||
}: {
|
||||
action: () => Promise<any>;
|
||||
routerAction?: "refresh" | "back";
|
||||
children: React.ReactNode;
|
||||
action: () => Promise<any>;
|
||||
routerAction?: "refresh" | "back" | "do-nothing";
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [pending, setPending] = useState(false);
|
||||
const router = useRouter();
|
||||
const [pending, setPending] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={async () => {
|
||||
if (pending) return;
|
||||
return (
|
||||
<button
|
||||
onClick={async () => {
|
||||
if (pending) return;
|
||||
|
||||
setPending(true);
|
||||
await callServerPromise(action());
|
||||
setPending(false);
|
||||
setPending(true);
|
||||
await callServerPromise(action());
|
||||
setPending(false);
|
||||
|
||||
if (routerAction === "back") {
|
||||
if (routerAction === "back") {
|
||||
router.back();
|
||||
router.refresh();
|
||||
}
|
||||
else if (routerAction === "refresh") router.refresh();
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children} {pending && <LoadingIcon />}
|
||||
</button>
|
||||
);
|
||||
} else if (routerAction === "refresh") router.refresh();
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children} {pending && <LoadingIcon />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function ButtonActionMenu(props: {
|
||||
title?: string;
|
||||
actions: {
|
||||
title: string;
|
||||
action: () => Promise<any>;
|
||||
}[];
|
||||
title?: string;
|
||||
actions: {
|
||||
title: string;
|
||||
action: () => Promise<any>;
|
||||
}[];
|
||||
}) {
|
||||
const user = useAuth();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const clerk = useClerk();
|
||||
const user = useAuth();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const clerk = useClerk();
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="gap-2" variant="outline" disabled={isLoading}>
|
||||
{props.title}
|
||||
{isLoading ? <LoadingIcon /> : <MoreVertical size={14} />}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
{props.actions.map((action) => (
|
||||
<DropdownMenuItem
|
||||
key={action.title}
|
||||
onClick={async () => {
|
||||
if (!user.isSignedIn) {
|
||||
clerk.openSignIn({
|
||||
redirectUrl: window.location.href,
|
||||
});
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="gap-2" variant="outline" disabled={isLoading}>
|
||||
{props.title}
|
||||
{isLoading ? <LoadingIcon /> : <MoreVertical size={14} />}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
{props.actions.map((action) => (
|
||||
<DropdownMenuItem
|
||||
key={action.title}
|
||||
onClick={async () => {
|
||||
if (!user.isSignedIn) {
|
||||
clerk.openSignIn({
|
||||
redirectUrl: window.location.href,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
await callServerPromise(action.action());
|
||||
setIsLoading(false);
|
||||
}}
|
||||
>
|
||||
{action.title}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
setIsLoading(true);
|
||||
await callServerPromise(action.action());
|
||||
setIsLoading(false);
|
||||
}}
|
||||
>
|
||||
{action.title}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,10 +13,7 @@ export async function CodeBlock(props: {
|
||||
|
||||
return (
|
||||
<div className="relative w-full text-sm">
|
||||
{/* max-w-[calc(32rem-1.5rem-1.5rem)] */}
|
||||
{/* <div className=""> */}
|
||||
<p
|
||||
// tabIndex={1}
|
||||
className="[&>pre]:p-4 rounded-lg max-h-96 overflow-auto w-full"
|
||||
style={{
|
||||
overflowWrap: "break-word",
|
||||
@@ -28,7 +25,6 @@ export async function CodeBlock(props: {
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
{/* </div> */}
|
||||
<CopyButton className="absolute right-2 top-2" text={props.code} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import { CopyButton } from "@/components/CopyButton";
|
||||
import type { StringLiteralUnion } from "shikiji";
|
||||
import useSWR from "swr";
|
||||
import { highlight } from "../server/highlight";
|
||||
|
||||
export function CodeBlockClient({
|
||||
code,
|
||||
lang,
|
||||
}: {
|
||||
code: string;
|
||||
lang: StringLiteralUnion<string>;
|
||||
}) {
|
||||
const { data } = useSWR(code, async () => {
|
||||
return highlight(code.trim(), lang);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative w-full text-sm">
|
||||
{data && (
|
||||
<p
|
||||
className="[&>pre]:p-4 rounded-lg max-h-96 overflow-auto w-full"
|
||||
style={{
|
||||
overflowWrap: "break-word",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<CopyButton className="absolute right-2 top-2" text={code} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,10 +7,12 @@ import { toast } from "sonner";
|
||||
|
||||
export function CopyButton({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
text: string;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Button
|
||||
@@ -21,7 +23,7 @@ export function CopyButton({
|
||||
}}
|
||||
className={cn(" p-2 min-h-0 aspect-square", className)}
|
||||
>
|
||||
<Copy size={14} />
|
||||
{children} <Copy size={14} />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export function CreateShareButton({
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const workflow_version_id = workflow?.versions.find(
|
||||
(x) => x.version === version,
|
||||
(x) => x.version == version,
|
||||
)?.id;
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"use client";
|
||||
import { getCurrentPlanWithAuth } from "@/server/getCurrentPlan";
|
||||
import * as React from "react";
|
||||
import { createContext } from "react";
|
||||
|
||||
export type CurrentPlanContextType = Awaited<
|
||||
ReturnType<typeof getCurrentPlanWithAuth>
|
||||
>;
|
||||
export const CurrentPlanContext = createContext<
|
||||
CurrentPlanContextType | undefined
|
||||
>(undefined);
|
||||
export function SubscriptionProvider({
|
||||
sub,
|
||||
children,
|
||||
}: {
|
||||
sub: CurrentPlanContextType;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<CurrentPlanContext.Provider value={sub}>
|
||||
{children}
|
||||
</CurrentPlanContext.Provider>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user