Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed659ebdb8 | ||
|
|
f6a1b88dda | ||
|
|
fed7b380b6 | ||
|
|
90cec6b778 | ||
|
|
7b61fea849 | ||
|
|
60471a8e01 |
@@ -1,7 +0,0 @@
|
|||||||
ARG VARIANT=18-bullseye
|
|
||||||
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:${VARIANT}
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
|
||||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|
||||||
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
|
||||||
|
|
||||||
RUN npm install -g bun
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
|
|
||||||
environment:
|
|
||||||
VSCODE_DEV_CONTAINER: true
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
# Forwards the local Docker socket to the container.
|
|
||||||
- /var/run/docker.sock:/var/run/docker-host.sock
|
|
||||||
# Update this to wherever you want VS Code to mount the folder of your project
|
|
||||||
- ../..:/workspaces:cached
|
|
||||||
|
|
||||||
# Overrides default command so things don't shut down after the process ends.
|
|
||||||
# entrypoint: /usr/local/share/docker-init.sh
|
|
||||||
command: sleep infinity
|
|
||||||
postgres:
|
|
||||||
image: "postgres:15.2-alpine"
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
POSTGRES_DB: verceldb
|
|
||||||
ports:
|
|
||||||
- "5480:5432"
|
|
||||||
pg_proxy:
|
|
||||||
image: ghcr.io/neondatabase/wsproxy:latest
|
|
||||||
environment:
|
|
||||||
APPEND_PORT: "postgres:5432"
|
|
||||||
ALLOW_ADDR_REGEX: ".*"
|
|
||||||
LOG_TRAFFIC: "true"
|
|
||||||
ports:
|
|
||||||
- "5481:80"
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
localstack:
|
|
||||||
image: localstack/localstack:latest
|
|
||||||
environment:
|
|
||||||
SERVICES: s3
|
|
||||||
ports:
|
|
||||||
- 4566:4566
|
|
||||||
volumes:
|
|
||||||
- ../web/aws:/etc/localstack/init/ready.d
|
|
||||||
- ../web/aws:/app/web/aws
|
|
||||||
|
|
||||||
Vendored
+3
-2
@@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"DavidAnson.vscode-markdownlint", // markdown linting
|
"DavidAnson.vscode-markdownlint", // markdown linting
|
||||||
|
"yzhang.markdown-all-in-one", // nicer markdown support
|
||||||
"esbenp.prettier-vscode", // prettier plugin
|
"esbenp.prettier-vscode", // prettier plugin
|
||||||
"dbaeumer.vscode-eslint", // eslint plugin
|
"dbaeumer.vscode-eslint", // eslint plugin
|
||||||
"bradlc.vscode-tailwindcss", // hinting / autocompletion for tailwind
|
"bradlc.vscode-tailwindcss", // hinting / autocompletion for tailwind
|
||||||
"ban.spellright", // Spell check for docs
|
"ban.spellright", // Spell check for docs
|
||||||
"stripe.vscode-stripe", // stripe VSCode extension
|
"stripe.vscode-stripe", // stripe VSCode extension
|
||||||
|
"Prisma.prisma", // syntax|format|completion for prisma
|
||||||
"rebornix.project-snippets", // Share useful snippets between collaborators
|
"rebornix.project-snippets", // Share useful snippets between collaborators
|
||||||
"inlang.vs-code-extension",
|
"inlang.vs-code-extension" // improved i18n DX
|
||||||
"biomejs.biome" // improved i18n DX
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
-3
@@ -1,9 +1,8 @@
|
|||||||
{
|
{
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": false,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"quickfix.biome": "explicit",
|
"source.fixAll.eslint": true
|
||||||
"source.organizeImports.biome": "explicit"
|
|
||||||
},
|
},
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
"spellright.language": ["en"],
|
"spellright.language": ["en"],
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from enum import Enum
|
|||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
from uuid import uuid4
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
@@ -19,6 +20,7 @@ from urllib.parse import parse_qs
|
|||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
from starlette.types import ASGIApp, Scope, Receive, Send
|
from starlette.types import ASGIApp, Scope, Receive, Send
|
||||||
|
|
||||||
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
# executor = ThreadPoolExecutor(max_workers=5)
|
# executor = ThreadPoolExecutor(max_workers=5)
|
||||||
@@ -224,6 +226,52 @@ async def websocket_endpoint(websocket: WebSocket, machine_id: str):
|
|||||||
# return {"Hello": "World"}
|
# return {"Hello": "World"}
|
||||||
|
|
||||||
|
|
||||||
|
class UploadBody(BaseModel):
|
||||||
|
download_url: str
|
||||||
|
volume_name: str
|
||||||
|
volume_id: str
|
||||||
|
# callback_url: str
|
||||||
|
|
||||||
|
@app.post("/upload_volume")
|
||||||
|
async def upload_checkpoint(body: UploadBody):
|
||||||
|
global last_activity_time
|
||||||
|
last_activity_time = time.time()
|
||||||
|
logger.info(f"Extended inactivity time to {global_timeout}")
|
||||||
|
|
||||||
|
download_url = body.download_url
|
||||||
|
volume_name = body.volume_name
|
||||||
|
# callback_url = body.callback_url
|
||||||
|
|
||||||
|
folder_path = f"/app/builds/{body.volume_id}"
|
||||||
|
|
||||||
|
cp_process = await asyncio.subprocess.create_subprocess_exec("cp", "-r", "/app/src/volume-builder", folder_path)
|
||||||
|
await cp_process.wait()
|
||||||
|
|
||||||
|
# Write the config file
|
||||||
|
config = {
|
||||||
|
"volume_names": {
|
||||||
|
volume_name: download_url
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
volume_name: f'/volumes/{uuid4()}'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
await asyncio.subprocess.create_subprocess_shell(
|
||||||
|
f"modal run app.py",
|
||||||
|
# stdout=asyncio.subprocess.PIPE,
|
||||||
|
# stderr=asyncio.subprocess.PIPE,
|
||||||
|
cwd=folder_path,
|
||||||
|
env={**os.environ, "COLUMNS": "10000"}
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(f"{folder_path}/config.py", "w") as f:
|
||||||
|
f.write("config = " + json.dumps(config))
|
||||||
|
|
||||||
|
# check that thi
|
||||||
|
return JSONResponse(status_code=200, content={"message": "Volume uploading", "build_machine_instance_id": fly_instance_id})
|
||||||
|
|
||||||
|
|
||||||
@app.post("/create")
|
@app.post("/create")
|
||||||
async def create_machine(item: Item):
|
async def create_machine(item: Item):
|
||||||
global last_activity_time
|
global last_activity_time
|
||||||
@@ -312,7 +360,9 @@ async def build_logic(item: Item):
|
|||||||
config = {
|
config = {
|
||||||
"name": item.name,
|
"name": item.name,
|
||||||
"deploy_test": os.environ.get("DEPLOY_TEST_FLAG", "False"),
|
"deploy_test": os.environ.get("DEPLOY_TEST_FLAG", "False"),
|
||||||
"gpu": item.gpu
|
"gpu": item.gpu,
|
||||||
|
"public_checkpoint_volume": "model-store",
|
||||||
|
"private_checkpoint_volume": "private-model-store"
|
||||||
}
|
}
|
||||||
with open(f"{folder_path}/config.py", "w") as f:
|
with open(f"{folder_path}/config.py", "w") as f:
|
||||||
f.write("config = " + json.dumps(config))
|
f.write("config = " + json.dumps(config))
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
from config import config
|
from config import config
|
||||||
import modal
|
import modal
|
||||||
from modal import Image, Mount, web_endpoint, Stub, asgi_app
|
from modal import Image, Mount, web_endpoint, Stub, asgi_app, Volume
|
||||||
import json
|
import json
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
from volume import volumes
|
||||||
|
|
||||||
# deploy_test = False
|
# deploy_test = False
|
||||||
|
|
||||||
@@ -28,7 +29,6 @@ web_app = FastAPI()
|
|||||||
print(config)
|
print(config)
|
||||||
print("deploy_test ", deploy_test)
|
print("deploy_test ", deploy_test)
|
||||||
stub = Stub(name=config["name"])
|
stub = Stub(name=config["name"])
|
||||||
# print(stub.app_id)
|
|
||||||
|
|
||||||
if not deploy_test:
|
if not deploy_test:
|
||||||
# dockerfile_image = Image.from_dockerfile(f"{current_directory}/Dockerfile", context_mount=Mount.from_local_dir(f"{current_directory}/data", remote_path="/data"))
|
# dockerfile_image = Image.from_dockerfile(f"{current_directory}/Dockerfile", context_mount=Mount.from_local_dir(f"{current_directory}/data", remote_path="/data"))
|
||||||
@@ -56,7 +56,7 @@ if not deploy_test:
|
|||||||
# # Install comfy deploy
|
# # Install comfy deploy
|
||||||
# "cd /comfyui/custom_nodes && git clone https://github.com/BennyKok/comfyui-deploy.git",
|
# "cd /comfyui/custom_nodes && git clone https://github.com/BennyKok/comfyui-deploy.git",
|
||||||
# )
|
# )
|
||||||
# .copy_local_file(f"{current_directory}/data/extra_model_paths.yaml", "/comfyui")
|
.copy_local_file(f"{current_directory}/data/extra_model_paths.yaml", "/comfyui")
|
||||||
|
|
||||||
.copy_local_file(f"{current_directory}/data/start.sh", "/start.sh")
|
.copy_local_file(f"{current_directory}/data/start.sh", "/start.sh")
|
||||||
.run_commands("chmod +x /start.sh")
|
.run_commands("chmod +x /start.sh")
|
||||||
@@ -153,8 +153,9 @@ image = Image.debian_slim()
|
|||||||
|
|
||||||
target_image = image if deploy_test else dockerfile_image
|
target_image = image if deploy_test else dockerfile_image
|
||||||
|
|
||||||
|
@stub.function(image=target_image, gpu=config["gpu"]
|
||||||
@stub.function(image=target_image, gpu=config["gpu"])
|
,volumes=volumes
|
||||||
|
)
|
||||||
def run(input: Input):
|
def run(input: Input):
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
@@ -163,6 +164,7 @@ def run(input: Input):
|
|||||||
|
|
||||||
command = ["python", "main.py",
|
command = ["python", "main.py",
|
||||||
"--disable-auto-launch", "--disable-metadata"]
|
"--disable-auto-launch", "--disable-metadata"]
|
||||||
|
|
||||||
server_process = subprocess.Popen(command, cwd="/comfyui")
|
server_process = subprocess.Popen(command, cwd="/comfyui")
|
||||||
|
|
||||||
check_server(
|
check_server(
|
||||||
@@ -235,7 +237,9 @@ async def bar(request_input: RequestInput):
|
|||||||
# pass
|
# pass
|
||||||
|
|
||||||
|
|
||||||
@stub.function(image=image)
|
@stub.function(image=image
|
||||||
|
,volumes=volumes
|
||||||
|
)
|
||||||
@asgi_app()
|
@asgi_app()
|
||||||
def comfyui_api():
|
def comfyui_api():
|
||||||
return web_app
|
return web_app
|
||||||
@@ -285,6 +289,7 @@ def spawn_comfyui_in_background():
|
|||||||
# to be on a single container.
|
# to be on a single container.
|
||||||
concurrency_limit=1,
|
concurrency_limit=1,
|
||||||
timeout=10 * 60,
|
timeout=10 * 60,
|
||||||
|
volumes=volumes,
|
||||||
)
|
)
|
||||||
@asgi_app()
|
@asgi_app()
|
||||||
def comfyui_app():
|
def comfyui_app():
|
||||||
@@ -303,4 +308,4 @@ def comfyui_app():
|
|||||||
},
|
},
|
||||||
)()
|
)()
|
||||||
|
|
||||||
return make_simple_proxy_app(ProxyContext(config))
|
return make_simple_proxy_app(ProxyContext(config))
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
config = {"name": "my-app", "deploy_test": "True", "gpu": "T4"}
|
config = {
|
||||||
|
"name": "my-app",
|
||||||
|
"deploy_test": "True",
|
||||||
|
"gpu": "T4",
|
||||||
|
"public_checkpoint_volume": "model-store",
|
||||||
|
"private_checkpoint_volume": "private-model-store"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,30 @@
|
|||||||
comfyui:
|
comfyui:
|
||||||
base_path: /runpod-volume/ComfyUI/
|
base_path: /extra_models/
|
||||||
checkpoints: models/checkpoints/
|
checkpoints: |
|
||||||
clip: models/clip/
|
checkpoints
|
||||||
clip_vision: models/clip_vision/
|
private_checkpoints
|
||||||
configs: models/configs/
|
clip: |
|
||||||
controlnet: models/controlnet/
|
clip
|
||||||
embeddings: models/embeddings/
|
private_clip
|
||||||
loras: models/loras/
|
clip_vision: |
|
||||||
upscale_models: models/upscale_models/
|
clip_vision
|
||||||
vae: models/vae/
|
private_clip_vision
|
||||||
|
configs: |
|
||||||
|
configs
|
||||||
|
private_configs
|
||||||
|
controlnet: |
|
||||||
|
controlnet
|
||||||
|
private_controlnet
|
||||||
|
embeddings: |
|
||||||
|
embeddings
|
||||||
|
private_embeddings
|
||||||
|
loras: |
|
||||||
|
loras
|
||||||
|
private_loras
|
||||||
|
upscale_models: |
|
||||||
|
upscale_models
|
||||||
|
private_upscale_models
|
||||||
|
vae: |
|
||||||
|
vae
|
||||||
|
private_vae
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
"""
|
||||||
|
This is a standalone script to download models into a modal Volume using civitai
|
||||||
|
|
||||||
|
Example Usage
|
||||||
|
`modal run insert_models::insert_model --civitai-url https://civitai.com/models/36520/ghostmix`
|
||||||
|
This inserts an individual model from a civitai url
|
||||||
|
|
||||||
|
`modal run insert_models::insert_models_civitai_api`
|
||||||
|
This inserts a bunch of models based on the models retrieved by civitai
|
||||||
|
|
||||||
|
civitai's API reference https://github.com/civitai/civitai/wiki/REST-API-Reference
|
||||||
|
"""
|
||||||
|
import modal
|
||||||
|
import subprocess
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
stub = modal.Stub()
|
||||||
|
|
||||||
|
# NOTE: volume name can be variable
|
||||||
|
volume = modal.Volume.persisted("rah")
|
||||||
|
model_store_path = "/vol/models"
|
||||||
|
MODEL_ROUTE = "models"
|
||||||
|
image = (
|
||||||
|
modal.Image.debian_slim().apt_install("wget").pip_install("requests")
|
||||||
|
)
|
||||||
|
|
||||||
|
@stub.function(volumes={model_store_path: volume}, image=image, timeout=50000, gpu=None)
|
||||||
|
def download_model(download_url):
|
||||||
|
print(download_url)
|
||||||
|
subprocess.run(["wget", download_url, "--content-disposition", "-P", model_store_path])
|
||||||
|
subprocess.run(["ls", "-la", model_store_path])
|
||||||
|
volume.commit()
|
||||||
|
|
||||||
|
# file is raw output from Civitai API https://github.com/civitai/civitai/wiki/REST-API-Reference
|
||||||
|
|
||||||
|
@stub.function()
|
||||||
|
def get_civitai_models(model_type: str, sort: str = "Highest Rated", page: int = 1):
|
||||||
|
"""Fetch models from CivitAI API based on type."""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"https://civitai.com/api/v1/models", params={"types": model_type, "page": page, "sort": sort})
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f"Error fetching models: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@stub.function()
|
||||||
|
def get_civitai_model_url(civitai_url: str):
|
||||||
|
# Validate the URL
|
||||||
|
|
||||||
|
if civitai_url.startswith("https://civitai.com/api/"):
|
||||||
|
api_url = civitai_url
|
||||||
|
elif civitai_url.startswith("https://civitai.com/models/"):
|
||||||
|
try:
|
||||||
|
model_id = civitai_url.split("/")[4]
|
||||||
|
int(model_id)
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
return None
|
||||||
|
api_url = f"https://civitai.com/api/v1/models/{model_id}"
|
||||||
|
else:
|
||||||
|
return "Error: URL must be from civitai.com and contain /models/"
|
||||||
|
|
||||||
|
response = requests.get(api_url)
|
||||||
|
# Check for successful response
|
||||||
|
if response.status_code != 200:
|
||||||
|
return f"Error: Unable to fetch data from {api_url}"
|
||||||
|
# Return the response data
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@stub.local_entrypoint()
|
||||||
|
def insert_models_civitai_api(type: str = "Checkpoint", sort = "Highest Rated", page: int = 1):
|
||||||
|
civitai_models = get_civitai_models.local(type, sort, page)
|
||||||
|
if civitai_models:
|
||||||
|
for _ in download_model.map(map(lambda model: model['modelVersions'][0]['downloadUrl'], civitai_models['items'])):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print("Failed to retrieve models.")
|
||||||
|
|
||||||
|
@stub.local_entrypoint()
|
||||||
|
def insert_model(civitai_url: str):
|
||||||
|
if civitai_url.startswith("'https://civitai.com/api/download/models/"):
|
||||||
|
download_url = civitai_url
|
||||||
|
else:
|
||||||
|
civitai_model = get_civitai_model_url.local(civitai_url)
|
||||||
|
if civitai_model:
|
||||||
|
download_url = civitai_model['modelVersions'][0]['downloadUrl']
|
||||||
|
else:
|
||||||
|
return "invalid URL"
|
||||||
|
|
||||||
|
download_model.remote(download_url)
|
||||||
|
|
||||||
|
@stub.local_entrypoint()
|
||||||
|
def simple_download():
|
||||||
|
download_urls = ['https://civitai.com/api/download/models/119057', 'https://civitai.com/api/download/models/130090', 'https://civitai.com/api/download/models/31859', 'https://civitai.com/api/download/models/128713', 'https://civitai.com/api/download/models/179657', 'https://civitai.com/api/download/models/143906', 'https://civitai.com/api/download/models/9208', 'https://civitai.com/api/download/models/136078', 'https://civitai.com/api/download/models/134065', 'https://civitai.com/api/download/models/288775', 'https://civitai.com/api/download/models/95263', 'https://civitai.com/api/download/models/288982', 'https://civitai.com/api/download/models/87153', 'https://civitai.com/api/download/models/10638', 'https://civitai.com/api/download/models/263809', 'https://civitai.com/api/download/models/130072', 'https://civitai.com/api/download/models/117019', 'https://civitai.com/api/download/models/95256', 'https://civitai.com/api/download/models/197181', 'https://civitai.com/api/download/models/256915', 'https://civitai.com/api/download/models/118945', 'https://civitai.com/api/download/models/125843', 'https://civitai.com/api/download/models/179015', 'https://civitai.com/api/download/models/245598', 'https://civitai.com/api/download/models/223670', 'https://civitai.com/api/download/models/90072', 'https://civitai.com/api/download/models/290817', 'https://civitai.com/api/download/models/154097', 'https://civitai.com/api/download/models/143497', 'https://civitai.com/api/download/models/5637']
|
||||||
|
|
||||||
|
for _ in download_model.map(download_urls):
|
||||||
|
pass
|
||||||
@@ -45,13 +45,13 @@ for package in packages:
|
|||||||
response = requests.request("POST", f"{root_url}/customnode/install", json=package, headers=headers)
|
response = requests.request("POST", f"{root_url}/customnode/install", json=package, headers=headers)
|
||||||
print(response.text)
|
print(response.text)
|
||||||
|
|
||||||
with open('models.json') as f:
|
# with open('models.json') as f:
|
||||||
models = json.load(f)
|
# models = json.load(f)
|
||||||
|
#
|
||||||
for model in models:
|
# for model in models:
|
||||||
response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers)
|
# response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers)
|
||||||
print(response.text)
|
# print(response.text)
|
||||||
|
|
||||||
# Close the server
|
# Close the server
|
||||||
server_process.terminate()
|
server_process.terminate()
|
||||||
print("Finished installing dependencies.")
|
print("Finished installing dependencies.")
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import modal
|
||||||
|
from config import config
|
||||||
|
|
||||||
|
public_model_volume = modal.Volume.persisted(config["public_checkpoint_volume"])
|
||||||
|
private_volume = modal.Volume.persisted(config["private_checkpoint_volume"])
|
||||||
|
|
||||||
|
BASEMODEL_DIR = "/extra_models/"
|
||||||
|
MODEL_DIR = BASEMODEL_DIR + "checkpoints"
|
||||||
|
PRIVATE_MODEL_DIR = BASEMODEL_DIR + "private_checkpoints"
|
||||||
|
volumes = {MODEL_DIR: public_model_volume, PRIVATE_MODEL_DIR: private_volume}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import modal
|
||||||
|
from config import config
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
stub = modal.Stub()
|
||||||
|
|
||||||
|
# Volume names may only contain alphanumeric characters, dashes, periods, and underscores, and must be less than 64 characters in length.
|
||||||
|
def is_valid_name(name: str) -> bool:
|
||||||
|
allowed_characters = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._")
|
||||||
|
return 0 < len(name) <= 64 and all(char in allowed_characters for char in name)
|
||||||
|
|
||||||
|
def create_volumes(volume_names, paths):
|
||||||
|
path_to_vol = {}
|
||||||
|
for volume_name in volume_names.keys():
|
||||||
|
if not is_valid_name(volume_name):
|
||||||
|
pass
|
||||||
|
modal_volume = modal.Volume.persisted(volume_name)
|
||||||
|
path_to_vol[paths[volume_name]] = modal_volume
|
||||||
|
|
||||||
|
return path_to_vol
|
||||||
|
|
||||||
|
vol_name_to_links = config["volume_names"]
|
||||||
|
vol_name_to_path = config["paths"]
|
||||||
|
volumes = create_volumes(vol_name_to_links, vol_name_to_path)
|
||||||
|
image = (
|
||||||
|
modal.Image.debian_slim().apt_install("wget").pip_install("requests")
|
||||||
|
)
|
||||||
|
|
||||||
|
print(vol_name_to_links)
|
||||||
|
print(vol_name_to_path)
|
||||||
|
print(volumes)
|
||||||
|
|
||||||
|
@stub.function(volumes=volumes, image=image, timeout=5000, gpu=None)
|
||||||
|
def download_model(volume_name, download_url):
|
||||||
|
model_store_path = vol_name_to_path[volume_name]
|
||||||
|
subprocess.run(["wget", download_url, "--content-disposition", "-P", model_store_path])
|
||||||
|
subprocess.run(["ls", "-la", model_store_path])
|
||||||
|
volumes[model_store_path].commit()
|
||||||
|
|
||||||
|
@stub.local_entrypoint()
|
||||||
|
def simple_download():
|
||||||
|
print(vol_name_to_links)
|
||||||
|
print([(vol_name, link) for vol_name,link in vol_name_to_links.items()])
|
||||||
|
list(download_model.starmap([(vol_name, link) for vol_name,link in vol_name_to_links.items()]))
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
config = {
|
||||||
|
"volume_names": {
|
||||||
|
"test": "https://pub-6230db03dc3a4861a9c3e55145ceda44.r2.dev/openpose-pose (1).png"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"test": "/volumes/something"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import folder_paths
|
|
||||||
from PIL import Image, ImageOps
|
|
||||||
import numpy as np
|
|
||||||
import torch
|
|
||||||
import folder_paths
|
|
||||||
|
|
||||||
|
|
||||||
class ComfyUIDeployExternalCheckpoints:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(s):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"input_id": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": "input_checkpoints"},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"default_checkpoints_name": (folder_paths.get_filename_list("checkpoints"), ),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = (folder_paths.get_filename_list("checkpoints"),)
|
|
||||||
RETURN_NAMES = ("path",)
|
|
||||||
|
|
||||||
FUNCTION = "run"
|
|
||||||
|
|
||||||
CATEGORY = "deploy"
|
|
||||||
|
|
||||||
def run(self, input_id, default_checkpoints_name=None):
|
|
||||||
import requests
|
|
||||||
import os
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
if input_id and input_id.startswith('http'):
|
|
||||||
unique_filename = str(uuid.uuid4()) + ".safetensors"
|
|
||||||
print(unique_filename)
|
|
||||||
print(folder_paths.folder_names_and_paths["checkpoints"][0][0])
|
|
||||||
destination_path = os.path.join(
|
|
||||||
folder_paths.folder_names_and_paths["checkpoints"][0][0], unique_filename)
|
|
||||||
print(destination_path)
|
|
||||||
print("Downloading external checkpoints - " +
|
|
||||||
input_id + " to " + destination_path)
|
|
||||||
response = requests.get(
|
|
||||||
input_id, headers={'User-Agent': 'Mozilla/5.0'}, allow_redirects=True)
|
|
||||||
with open(destination_path, 'wb') as out_file:
|
|
||||||
out_file.write(response.content)
|
|
||||||
return (unique_filename,)
|
|
||||||
else:
|
|
||||||
return (default_checkpoints_name,)
|
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
|
||||||
"ComfyUIDeployExternalCheckpoints": ComfyUIDeployExternalCheckpoints}
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
||||||
"ComfyUIDeployExternalCheckpoints": "External Checkpoints (ComfyUI Deploy)"}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
**/node_modules
|
||||||
|
**/.next
|
||||||
|
**/public
|
||||||
|
packages/prisma/zod
|
||||||
|
apps/web/public/embed
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/** @type {import("eslint").Linter.Config} */
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: [
|
||||||
|
// "plugin:playwright/playwright-test",
|
||||||
|
"next",
|
||||||
|
// "next/core-web-vitals",
|
||||||
|
"plugin:prettier/recommended",
|
||||||
|
// "turbo",
|
||||||
|
// "plugin:you-dont-need-lodash-underscore/compatible-warn",
|
||||||
|
],
|
||||||
|
plugins: ["unused-imports"],
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ["./tsconfig.json"],
|
||||||
|
// project: ["./apps/*/tsconfig.json", "./packages/*/tsconfig.json"],
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
next: {
|
||||||
|
// rootDir: ["apps/*/", "packages/*/"],
|
||||||
|
rootDir: ["src"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"@next/next/no-img-element": "off",
|
||||||
|
"@next/next/no-html-link-for-pages": "off",
|
||||||
|
"jsx-a11y/role-supports-aria-props": "off", // @see https://github.com/vercel/next.js/issues/27989#issuecomment-897638654
|
||||||
|
// "playwright/no-page-pause": "error",
|
||||||
|
"react/jsx-curly-brace-presence": [
|
||||||
|
"error",
|
||||||
|
{ props: "never", children: "never" },
|
||||||
|
],
|
||||||
|
"react/self-closing-comp": ["error", { component: true, html: true }],
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
vars: "all",
|
||||||
|
varsIgnorePattern: "^_",
|
||||||
|
args: "after-used",
|
||||||
|
argsIgnorePattern: "^_",
|
||||||
|
destructuredArrayIgnorePattern: "^_",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
patterns: ["lodash"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"prefer-template": "error",
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ["*.ts", "*.tsx"],
|
||||||
|
extends: [
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
// "plugin:@calcom/eslint/recommended",
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
"@typescript-eslint",
|
||||||
|
// "@calcom/eslint"
|
||||||
|
],
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/consistent-type-imports": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
prefer: "type-imports",
|
||||||
|
// TODO: enable this once prettier supports it
|
||||||
|
// fixStyle: "inline-type-imports",
|
||||||
|
fixStyle: "separate-type-imports",
|
||||||
|
disallowTypeAnnotations: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// overrides: [
|
||||||
|
// {
|
||||||
|
// files: ["**/playwright/**/*.{tsx,ts}"],
|
||||||
|
// rules: {
|
||||||
|
// "@typescript-eslint/no-unused-vars": "off",
|
||||||
|
// "no-undef": "off",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// files: ["**/playwright/**/*.{js,jsx}"],
|
||||||
|
// rules: {
|
||||||
|
// "@typescript-eslint/no-unused-vars": "off",
|
||||||
|
// "no-undef": "off",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/1.5.2/schema.json",
|
|
||||||
"organizeImports": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"enabled": true,
|
|
||||||
"rules": {
|
|
||||||
"recommended": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"json": {
|
|
||||||
"parser": {
|
|
||||||
"allowComments": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vcs": {
|
|
||||||
"enabled": true,
|
|
||||||
"clientKind": "git",
|
|
||||||
"useIgnoreFile": true,
|
|
||||||
"defaultBranch": "main"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "started_at" timestamp;
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
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', 'failed', 'succeded');
|
||||||
|
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,
|
||||||
|
"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,
|
||||||
|
"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_workflow_runs_id_fk" FOREIGN KEY ("checkpoint_volume_id") REFERENCES "comfyui_deploy"."workflow_runs"("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,2 @@
|
|||||||
|
ALTER TYPE "resource_upload" ADD VALUE 'error';--> statement-breakpoint
|
||||||
|
ALTER TABLE "comfyui_deploy"."checkpoints" ADD COLUMN "build_log" text;
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "comfyui_deploy"."deployments" ADD COLUMN "share_slug" text;--> statement-breakpoint
|
|
||||||
ALTER TABLE "comfyui_deploy"."deployments" ADD CONSTRAINT "deployments_share_slug_unique" UNIQUE("share_slug");
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS "comfyui_deploy"."user_usage" (
|
|
||||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
||||||
"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
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "comfyui_deploy"."user_usage" ADD CONSTRAINT "user_usage_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
@@ -1,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;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"id": "1ca4fdb7-c0c4-4c39-8b47-f40282293da0",
|
"id": "4d5b29d0-848f-4c2e-a2cd-2932f1fa38c6",
|
||||||
"prevId": "db06ea66-92c2-4ebe-93c1-6cb8a90ccd8b",
|
"prevId": "db06ea66-92c2-4ebe-93c1-6cb8a90ccd8b",
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"dialect": "pg",
|
"dialect": "pg",
|
||||||
@@ -88,6 +88,238 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"checkpoints": {
|
||||||
|
"name": "checkpoints",
|
||||||
|
"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": false
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"checkpoint_volume_id": {
|
||||||
|
"name": "checkpoint_volume_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"model_name": {
|
||||||
|
"name": "model_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_id": {
|
||||||
|
"name": "civitai_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_version_id": {
|
||||||
|
"name": "civitai_version_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_url": {
|
||||||
|
"name": "civitai_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_download_url": {
|
||||||
|
"name": "civitai_download_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_model_response": {
|
||||||
|
"name": "civitai_model_response",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"hf_url": {
|
||||||
|
"name": "hf_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"s3_url": {
|
||||||
|
"name": "s3_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"client_url": {
|
||||||
|
"name": "client_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"is_public": {
|
||||||
|
"name": "is_public",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "resource_upload",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'started'"
|
||||||
|
},
|
||||||
|
"upload_machine_id": {
|
||||||
|
"name": "upload_machine_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"upload_type": {
|
||||||
|
"name": "upload_type",
|
||||||
|
"type": "model_upload_type",
|
||||||
|
"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": {
|
||||||
|
"checkpoints_user_id_users_id_fk": {
|
||||||
|
"name": "checkpoints_user_id_users_id_fk",
|
||||||
|
"tableFrom": "checkpoints",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"checkpoints_checkpoint_volume_id_workflow_runs_id_fk": {
|
||||||
|
"name": "checkpoints_checkpoint_volume_id_workflow_runs_id_fk",
|
||||||
|
"tableFrom": "checkpoints",
|
||||||
|
"tableTo": "workflow_runs",
|
||||||
|
"columnsFrom": [
|
||||||
|
"checkpoint_volume_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"checkpoint_volume": {
|
||||||
|
"name": "checkpoint_volume",
|
||||||
|
"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": false
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"volume_name": {
|
||||||
|
"name": "volume_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()"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"name": "disabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"checkpoint_volume_user_id_users_id_fk": {
|
||||||
|
"name": "checkpoint_volume_user_id_users_id_fk",
|
||||||
|
"tableFrom": "checkpoint_volume",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
"deployments": {
|
"deployments": {
|
||||||
"name": "deployments",
|
"name": "deployments",
|
||||||
"schema": "comfyui_deploy",
|
"schema": "comfyui_deploy",
|
||||||
@@ -506,12 +738,6 @@
|
|||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
"default": "now()"
|
"default": "now()"
|
||||||
},
|
|
||||||
"started_at": {
|
|
||||||
"name": "started_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
@@ -732,6 +958,22 @@
|
|||||||
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"model_upload_type": {
|
||||||
|
"name": "model_upload_type",
|
||||||
|
"values": {
|
||||||
|
"civitai": "civitai",
|
||||||
|
"huggingface": "huggingface",
|
||||||
|
"other": "other"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resource_upload": {
|
||||||
|
"name": "resource_upload",
|
||||||
|
"values": {
|
||||||
|
"started": "started",
|
||||||
|
"failed": "failed",
|
||||||
|
"succeded": "succeded"
|
||||||
|
}
|
||||||
|
},
|
||||||
"workflow_run_origin": {
|
"workflow_run_origin": {
|
||||||
"name": "workflow_run_origin",
|
"name": "workflow_run_origin",
|
||||||
"values": {
|
"values": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "1425ee00-66fb-4541-8da7-19b217944545",
|
"id": "fed3e81d-c0d7-4deb-a63a-370039fb5edc",
|
||||||
"prevId": "1ca4fdb7-c0c4-4c39-8b47-f40282293da0",
|
"prevId": "4d5b29d0-848f-4c2e-a2cd-2932f1fa38c6",
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"dialect": "pg",
|
"dialect": "pg",
|
||||||
"tables": {
|
"tables": {
|
||||||
@@ -88,6 +88,244 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"checkpoints": {
|
||||||
|
"name": "checkpoints",
|
||||||
|
"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": false
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"checkpoint_volume_id": {
|
||||||
|
"name": "checkpoint_volume_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"model_name": {
|
||||||
|
"name": "model_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_id": {
|
||||||
|
"name": "civitai_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_version_id": {
|
||||||
|
"name": "civitai_version_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_url": {
|
||||||
|
"name": "civitai_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_download_url": {
|
||||||
|
"name": "civitai_download_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"civitai_model_response": {
|
||||||
|
"name": "civitai_model_response",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"hf_url": {
|
||||||
|
"name": "hf_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"s3_url": {
|
||||||
|
"name": "s3_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"client_url": {
|
||||||
|
"name": "client_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"is_public": {
|
||||||
|
"name": "is_public",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "resource_upload",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'started'"
|
||||||
|
},
|
||||||
|
"upload_machine_id": {
|
||||||
|
"name": "upload_machine_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"upload_type": {
|
||||||
|
"name": "upload_type",
|
||||||
|
"type": "model_upload_type",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"build_log": {
|
||||||
|
"name": "build_log",
|
||||||
|
"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": {
|
||||||
|
"checkpoints_user_id_users_id_fk": {
|
||||||
|
"name": "checkpoints_user_id_users_id_fk",
|
||||||
|
"tableFrom": "checkpoints",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"checkpoints_checkpoint_volume_id_workflow_runs_id_fk": {
|
||||||
|
"name": "checkpoints_checkpoint_volume_id_workflow_runs_id_fk",
|
||||||
|
"tableFrom": "checkpoints",
|
||||||
|
"tableTo": "workflow_runs",
|
||||||
|
"columnsFrom": [
|
||||||
|
"checkpoint_volume_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"checkpoint_volume": {
|
||||||
|
"name": "checkpoint_volume",
|
||||||
|
"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": false
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"volume_name": {
|
||||||
|
"name": "volume_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()"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"name": "disabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"checkpoint_volume_user_id_users_id_fk": {
|
||||||
|
"name": "checkpoint_volume_user_id_users_id_fk",
|
||||||
|
"tableFrom": "checkpoint_volume",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
"deployments": {
|
"deployments": {
|
||||||
"name": "deployments",
|
"name": "deployments",
|
||||||
"schema": "comfyui_deploy",
|
"schema": "comfyui_deploy",
|
||||||
@@ -129,12 +367,6 @@
|
|||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
"share_slug": {
|
|
||||||
"name": "share_slug",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
"description": {
|
||||||
"name": "description",
|
"name": "description",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -224,15 +456,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"compositePrimaryKeys": {},
|
"compositePrimaryKeys": {},
|
||||||
"uniqueConstraints": {
|
"uniqueConstraints": {}
|
||||||
"deployments_share_slug_unique": {
|
|
||||||
"name": "deployments_share_slug_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"share_slug"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"machines": {
|
"machines": {
|
||||||
"name": "machines",
|
"name": "machines",
|
||||||
@@ -520,12 +744,6 @@
|
|||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
"default": "now()"
|
"default": "now()"
|
||||||
},
|
|
||||||
"started_at": {
|
|
||||||
"name": "started_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
@@ -746,6 +964,22 @@
|
|||||||
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"model_upload_type": {
|
||||||
|
"name": "model_upload_type",
|
||||||
|
"values": {
|
||||||
|
"civitai": "civitai",
|
||||||
|
"huggingface": "huggingface",
|
||||||
|
"other": "other"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resource_upload": {
|
||||||
|
"name": "resource_upload",
|
||||||
|
"values": {
|
||||||
|
"started": "started",
|
||||||
|
"error": "error",
|
||||||
|
"succeded": "succeded"
|
||||||
|
}
|
||||||
|
},
|
||||||
"workflow_run_origin": {
|
"workflow_run_origin": {
|
||||||
"name": "workflow_run_origin",
|
"name": "workflow_run_origin",
|
||||||
"values": {
|
"values": {
|
||||||
|
|||||||
@@ -1,834 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "91bb0461-452a-4e59-abf4-8757fcd75a89",
|
|
||||||
"prevId": "1425ee00-66fb-4541-8da7-19b217944545",
|
|
||||||
"version": "5",
|
|
||||||
"dialect": "pg",
|
|
||||||
"tables": {
|
|
||||||
"api_keys": {
|
|
||||||
"name": "api_keys",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"key": {
|
|
||||||
"name": "key",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"revoked": {
|
|
||||||
"name": "revoked",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"api_keys_user_id_users_id_fk": {
|
|
||||||
"name": "api_keys_user_id_users_id_fk",
|
|
||||||
"tableFrom": "api_keys",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"api_keys_key_unique": {
|
|
||||||
"name": "api_keys_key_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"key"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"deployments": {
|
|
||||||
"name": "deployments",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"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()"
|
|
||||||
},
|
|
||||||
"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",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_run_outputs": {
|
|
||||||
"name": "workflow_run_outputs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"run_id": {
|
|
||||||
"name": "run_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"name": "data",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
|
|
||||||
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
|
|
||||||
"tableFrom": "workflow_run_outputs",
|
|
||||||
"tableTo": "workflow_runs",
|
|
||||||
"columnsFrom": [
|
|
||||||
"run_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_runs": {
|
|
||||||
"name": "workflow_runs",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_inputs": {
|
|
||||||
"name": "workflow_inputs",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"origin": {
|
|
||||||
"name": "origin",
|
|
||||||
"type": "workflow_run_origin",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'api'"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"name": "status",
|
|
||||||
"type": "workflow_run_status",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'not-started'"
|
|
||||||
},
|
|
||||||
"ended_at": {
|
|
||||||
"name": "ended_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"started_at": {
|
|
||||||
"name": "started_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflow_versions",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_version_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_runs_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"workflow_runs_machine_id_machines_id_fk": {
|
|
||||||
"name": "workflow_runs_machine_id_machines_id_fk",
|
|
||||||
"tableFrom": "workflow_runs",
|
|
||||||
"tableTo": "machines",
|
|
||||||
"columnsFrom": [
|
|
||||||
"machine_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "set null",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflows": {
|
|
||||||
"name": "workflows",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflows_user_id_users_id_fk": {
|
|
||||||
"name": "workflows_user_id_users_id_fk",
|
|
||||||
"tableFrom": "workflows",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"workflow_versions": {
|
|
||||||
"name": "workflow_versions",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"workflow": {
|
|
||||||
"name": "workflow",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_api": {
|
|
||||||
"name": "workflow_api",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"name": "version",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"snapshot": {
|
|
||||||
"name": "snapshot",
|
|
||||||
"type": "jsonb",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"workflow_versions_workflow_id_workflows_id_fk": {
|
|
||||||
"name": "workflow_versions_workflow_id_workflows_id_fk",
|
|
||||||
"tableFrom": "workflow_versions",
|
|
||||||
"tableTo": "workflows",
|
|
||||||
"columnsFrom": [
|
|
||||||
"workflow_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"deployment_environment": {
|
|
||||||
"name": "deployment_environment",
|
|
||||||
"values": {
|
|
||||||
"staging": "staging",
|
|
||||||
"production": "production",
|
|
||||||
"public-share": "public-share"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_gpu": {
|
|
||||||
"name": "machine_gpu",
|
|
||||||
"values": {
|
|
||||||
"T4": "T4",
|
|
||||||
"A10G": "A10G",
|
|
||||||
"A100": "A100"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_status": {
|
|
||||||
"name": "machine_status",
|
|
||||||
"values": {
|
|
||||||
"ready": "ready",
|
|
||||||
"building": "building",
|
|
||||||
"error": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"machine_type": {
|
|
||||||
"name": "machine_type",
|
|
||||||
"values": {
|
|
||||||
"classic": "classic",
|
|
||||||
"runpod-serverless": "runpod-serverless",
|
|
||||||
"modal-serverless": "modal-serverless",
|
|
||||||
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"workflow_run_origin": {
|
|
||||||
"name": "workflow_run_origin",
|
|
||||||
"values": {
|
|
||||||
"manual": "manual",
|
|
||||||
"api": "api",
|
|
||||||
"public-share": "public-share"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"workflow_run_status": {
|
|
||||||
"name": "workflow_run_status",
|
|
||||||
"values": {
|
|
||||||
"not-started": "not-started",
|
|
||||||
"running": "running",
|
|
||||||
"uploading": "uploading",
|
|
||||||
"success": "success",
|
|
||||||
"failed": "failed"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"schemas": {
|
|
||||||
"comfyui_deploy": "comfyui_deploy"
|
|
||||||
},
|
|
||||||
"_meta": {
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {},
|
|
||||||
"columns": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,842 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "fad17dc9-86c5-4081-8e73-47c113f48936",
|
|
||||||
"prevId": "91bb0461-452a-4e59-abf4-8757fcd75a89",
|
|
||||||
"version": "5",
|
|
||||||
"dialect": "pg",
|
|
||||||
"tables": {
|
|
||||||
"api_keys": {
|
|
||||||
"name": "api_keys",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"key": {
|
|
||||||
"name": "key",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"revoked": {
|
|
||||||
"name": "revoked",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"api_keys_user_id_users_id_fk": {
|
|
||||||
"name": "api_keys_user_id_users_id_fk",
|
|
||||||
"tableFrom": "api_keys",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"api_keys_key_unique": {
|
|
||||||
"name": "api_keys_key_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"key"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"deployments": {
|
|
||||||
"name": "deployments",
|
|
||||||
"schema": "comfyui_deploy",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"org_id": {
|
|
||||||
"name": "org_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"workflow_version_id": {
|
|
||||||
"name": "workflow_version_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"workflow_id": {
|
|
||||||
"name": "workflow_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"machine_id": {
|
|
||||||
"name": "machine_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"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": {
|
|
||||||
"\"comfyui_deploy\".\"user_usage\".\"updated_at\"": "\"comfyui_deploy\".\"user_usage\".\"ended_at\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -222,29 +222,15 @@
|
|||||||
{
|
{
|
||||||
"idx": 31,
|
"idx": 31,
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"when": 1705763980972,
|
"when": 1705975916818,
|
||||||
"tag": "0031_fast_lyja",
|
"tag": "0031_safe_multiple_man",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 32,
|
"idx": 32,
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"when": 1705806921697,
|
"when": 1705979098372,
|
||||||
"tag": "0032_shallow_vermin",
|
"tag": "0032_material_wallflower",
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 33,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1705824362978,
|
|
||||||
"tag": "0033_fantastic_marvel_boy",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 34,
|
|
||||||
"version": "5",
|
|
||||||
"when": 1705840184127,
|
|
||||||
"tag": "0034_previous_viper",
|
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+3
-4
@@ -12,11 +12,10 @@ let sslMode: string | boolean = process.env.SSL || "require";
|
|||||||
|
|
||||||
if (sslMode === "false") sslMode = false;
|
if (sslMode === "false") sslMode = false;
|
||||||
|
|
||||||
let connectionString = process.env.POSTGRES_URL!;
|
console.log(migrationsFolderName, sslMode);
|
||||||
|
|
||||||
const isDevContainer = process.env.VSCODE_DEV_CONTAINER !== undefined;
|
|
||||||
if (isDevContainer) connectionString = connectionString.replace("localhost","host.docker.internal")
|
|
||||||
|
|
||||||
|
const connectionString = process.env.POSTGRES_URL!;
|
||||||
|
console.log(connectionString);
|
||||||
const sql = postgres(connectionString, { max: 1, ssl: sslMode as any });
|
const sql = postgres(connectionString, { max: 1, ssl: sslMode as any });
|
||||||
const db = drizzle(sql, {
|
const db = drizzle(sql, {
|
||||||
logger: true,
|
logger: true,
|
||||||
|
|||||||
+11
-4
@@ -12,8 +12,7 @@
|
|||||||
"migrate-production": "bun run migrate.mts",
|
"migrate-production": "bun run migrate.mts",
|
||||||
"migrate-local": "SSL=false LOCAL=true bun run migrate.mts",
|
"migrate-local": "SSL=false LOCAL=true bun run migrate.mts",
|
||||||
"db-up": "docker-compose up",
|
"db-up": "docker-compose up",
|
||||||
"db-dev": "bun run db-up && bun run migrate-local",
|
"db-dev": "bun run db-up && bun run migrate-local"
|
||||||
"lint:fix": "bunx @biomejs/biome lint --apply ./src"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@algolia/autocomplete-core": "^1.13.0",
|
"@algolia/autocomplete-core": "^1.13.0",
|
||||||
@@ -26,7 +25,6 @@
|
|||||||
"@hono/zod-openapi": "^0.9.5",
|
"@hono/zod-openapi": "^0.9.5",
|
||||||
"@hono/zod-validator": "^0.1.11",
|
"@hono/zod-validator": "^0.1.11",
|
||||||
"@hookform/resolvers": "^3.3.2",
|
"@hookform/resolvers": "^3.3.2",
|
||||||
"@lemonsqueezy/lemonsqueezy.js": "^1.2.5",
|
|
||||||
"@mdx-js/loader": "^3.0.0",
|
"@mdx-js/loader": "^3.0.0",
|
||||||
"@mdx-js/react": "^3.0.0",
|
"@mdx-js/react": "^3.0.0",
|
||||||
"@neondatabase/serverless": "^0.6.0",
|
"@neondatabase/serverless": "^0.6.0",
|
||||||
@@ -107,17 +105,26 @@
|
|||||||
"zustand": "^4.4.7"
|
"zustand": "^4.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.5.2",
|
"@trivago/prettier-plugin-sort-imports": "4.1.1",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||||
|
"@typescript-eslint/parser": "^6.13.2",
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"drizzle-kit": "^0.20.6",
|
"drizzle-kit": "^0.20.6",
|
||||||
"eslint": "8.34.0",
|
"eslint": "8.34.0",
|
||||||
|
"eslint-config-next": "^14.0.4",
|
||||||
|
"eslint-config-prettier": "^8.6.0",
|
||||||
|
"eslint-config-turbo": "latest",
|
||||||
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
|
"eslint-plugin-unused-imports": "^3.0.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"postgres": "^3.4.3",
|
"postgres": "^3.4.3",
|
||||||
|
"prettier": "2.8.6",
|
||||||
|
"prettier-plugin-tailwindcss": "0.2.5",
|
||||||
"sharp": "^0.33.1",
|
"sharp": "^0.33.1",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.3.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import {
|
import { workflowRunOutputs, workflowRunsTable } from "@/db/schema";
|
||||||
userUsageTable,
|
|
||||||
workflowRunOutputs,
|
|
||||||
workflowRunsTable,
|
|
||||||
workflowTable,
|
|
||||||
} from "@/db/schema";
|
|
||||||
import { getDuration } from "@/lib/getRelativeTime";
|
|
||||||
import { getSubscription, setUsage } from "@/server/linkToPricing";
|
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -34,6 +27,7 @@ export async function POST(request: Request) {
|
|||||||
data: output_data,
|
data: output_data,
|
||||||
});
|
});
|
||||||
} else if (status) {
|
} else if (status) {
|
||||||
|
// console.log("status", status);
|
||||||
const workflow_run = await db
|
const workflow_run = await db
|
||||||
.update(workflowRunsTable)
|
.update(workflowRunsTable)
|
||||||
.set({
|
.set({
|
||||||
@@ -41,15 +35,8 @@ export async function POST(request: Request) {
|
|||||||
ended_at:
|
ended_at:
|
||||||
status === "success" || status === "failed" ? new Date() : null,
|
status === "success" || status === "failed" ? new Date() : null,
|
||||||
})
|
})
|
||||||
.where(eq(workflowRunsTable.id, run_id));
|
.where(eq(workflowRunsTable.id, run_id))
|
||||||
|
.returning();
|
||||||
// get data from workflowRunsTable
|
|
||||||
const userUsageTime = await importUserUsageData(run_id);
|
|
||||||
|
|
||||||
if (userUsageTime) {
|
|
||||||
// get the usage_time from userUsage
|
|
||||||
await addSubscriptionUnit(userUsageTime);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// const workflow_version = await db.query.workflowVersionTable.findFirst({
|
// const workflow_version = await db.query.workflowVersionTable.findFirst({
|
||||||
@@ -67,47 +54,3 @@ export async function POST(request: Request) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addSubscriptionUnit(userUsageTime: number) {
|
|
||||||
const subscription = await getSubscription();
|
|
||||||
|
|
||||||
// round up userUsageTime to the nearest integer
|
|
||||||
const roundedUsageTime = Math.ceil(userUsageTime);
|
|
||||||
|
|
||||||
if (subscription) {
|
|
||||||
const usage = await setUsage(
|
|
||||||
subscription.data[0].attributes.first_subscription_item.id,
|
|
||||||
roundedUsageTime
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||||
|
import { db } from "@/db/db";
|
||||||
|
import { checkpointTable, machinesTable } from "@/db/schema";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const Request = z.object({
|
||||||
|
machine_id: z.string(),
|
||||||
|
endpoint: z.string().optional(),
|
||||||
|
build_log: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const [data, error] = await parseDataSafe(Request, request);
|
||||||
|
if (!data || error) return error;
|
||||||
|
|
||||||
|
// console.log(data);
|
||||||
|
|
||||||
|
const { machine_id, endpoint, build_log } = data;
|
||||||
|
|
||||||
|
if (endpoint) {
|
||||||
|
await db
|
||||||
|
.update(checkpointTable)
|
||||||
|
.set({
|
||||||
|
// status: "ready",
|
||||||
|
// endpoint: endpoint,
|
||||||
|
// build_log: build_log,
|
||||||
|
})
|
||||||
|
.where(eq(machinesTable.id, machine_id));
|
||||||
|
} else {
|
||||||
|
// console.log(data);
|
||||||
|
await db
|
||||||
|
.update(machinesTable)
|
||||||
|
.set({
|
||||||
|
// status: "error",
|
||||||
|
// build_log: build_log,
|
||||||
|
})
|
||||||
|
.where(eq(machinesTable.id, machine_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
message: "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { GpuPricingPlan } from "@/app/(app)/pricing/components/gpuPricingTable";
|
|
||||||
import PricingList from "@/app/(app)/pricing/components/pricePlanList";
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<PricingList />
|
|
||||||
<GpuPricingPlan />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,11 @@ import { ButtonActionMenu } from "@/components/ButtonActionLoader";
|
|||||||
import { RunWorkflowInline } from "@/components/RunWorkflowInline";
|
import { RunWorkflowInline } from "@/components/RunWorkflowInline";
|
||||||
import { PublicRunOutputs } from "@/components/VersionSelect";
|
import { PublicRunOutputs } from "@/components/VersionSelect";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import { usersTable } from "@/db/schema";
|
import { usersTable } from "@/db/schema";
|
||||||
@@ -14,9 +14,9 @@ import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow";
|
|||||||
import { getRelativeTime } from "@/lib/getRelativeTime";
|
import { getRelativeTime } from "@/lib/getRelativeTime";
|
||||||
import { setInitialUserData } from "@/lib/setInitialUserData";
|
import { setInitialUserData } from "@/lib/setInitialUserData";
|
||||||
import {
|
import {
|
||||||
cloneMachine,
|
cloneMachine,
|
||||||
cloneWorkflow,
|
cloneWorkflow,
|
||||||
findSharedDeployment,
|
findSharedDeployment,
|
||||||
} from "@/server/curdDeploments";
|
} from "@/server/curdDeploments";
|
||||||
import { auth, clerkClient } from "@clerk/nextjs/server";
|
import { auth, clerkClient } from "@clerk/nextjs/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
@@ -25,87 +25,89 @@ import { redirect } from "next/navigation";
|
|||||||
export const maxDuration = 300; // 5 minutes
|
export const maxDuration = 300; // 5 minutes
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
params: { share_id: string };
|
params: { share_id: string };
|
||||||
}) {
|
}) {
|
||||||
const { userId } = await auth();
|
const { userId } = await auth();
|
||||||
|
|
||||||
// If there is user, check if the user data is present
|
// If there is user, check if the user data is present
|
||||||
if (userId) {
|
if (userId) {
|
||||||
const user = await db.query.usersTable.findFirst({
|
const user = await db.query.usersTable.findFirst({
|
||||||
where: eq(usersTable.id, userId),
|
where: eq(usersTable.id, userId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
await setInitialUserData(userId);
|
await setInitialUserData(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sharedDeployment = await findSharedDeployment(params.share_id);
|
const sharedDeployment = await findSharedDeployment(params.share_id);
|
||||||
|
|
||||||
if (!sharedDeployment) return redirect("/");
|
if (!sharedDeployment) return redirect("/");
|
||||||
|
|
||||||
const userName = sharedDeployment.workflow.org_id
|
const userName = sharedDeployment.workflow.org_id
|
||||||
? await clerkClient.organizations
|
? await clerkClient.organizations
|
||||||
.getOrganization({
|
.getOrganization({
|
||||||
organizationId: sharedDeployment.workflow.org_id,
|
organizationId: sharedDeployment.workflow.org_id,
|
||||||
})
|
})
|
||||||
.then((x) => x.name)
|
.then((x) => x.name)
|
||||||
: sharedDeployment.user.name;
|
: sharedDeployment.user.name;
|
||||||
|
|
||||||
const inputs = getInputsFromWorkflow(sharedDeployment.version);
|
const inputs = getInputsFromWorkflow(sharedDeployment.version);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-4 w-full grid grid-rows-[1fr,1fr] lg:grid-cols-[minmax(auto,500px),1fr] gap-4 max-h-[calc(100dvh-100px)]">
|
<div className="mt-4 w-full grid grid-rows-[1fr,1fr] lg:grid-cols-[minmax(auto,500px),1fr] gap-4 max-h-[calc(100dvh-100px)]">
|
||||||
<Card className="w-full h-fit mt-4">
|
<Card className="w-full h-fit mt-4">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex justify-between items-center">
|
<CardTitle className="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
{userName}
|
{userName}
|
||||||
{" / "}
|
{" / "}
|
||||||
{sharedDeployment.workflow.name}
|
{sharedDeployment.workflow.name}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ButtonActionMenu
|
<ButtonActionMenu
|
||||||
title="Clone"
|
title="Clone"
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
title: "Workflow",
|
title: "Workflow",
|
||||||
action: cloneWorkflow.bind(null, sharedDeployment.id),
|
action: cloneWorkflow.bind(null, sharedDeployment.id),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Machine",
|
title: "Machine",
|
||||||
action: cloneMachine.bind(null, sharedDeployment.id),
|
action: cloneMachine.bind(null, sharedDeployment.id),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription suppressHydrationWarning={true}>
|
<CardDescription suppressHydrationWarning={true}>
|
||||||
{getRelativeTime(sharedDeployment?.updated_at)}
|
{getRelativeTime(sharedDeployment?.updated_at)}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div>
|
<div>
|
||||||
{sharedDeployment?.description && sharedDeployment?.description}
|
{sharedDeployment?.description && (
|
||||||
</div>
|
<>{sharedDeployment?.description}</>
|
||||||
<RunWorkflowInline
|
)}
|
||||||
inputs={inputs}
|
</div>
|
||||||
machine_id={sharedDeployment.machine_id}
|
<RunWorkflowInline
|
||||||
workflow_version_id={sharedDeployment.workflow_version_id}
|
inputs={inputs}
|
||||||
/>
|
machine_id={sharedDeployment.machine_id}
|
||||||
</CardContent>
|
workflow_version_id={sharedDeployment.workflow_version_id}
|
||||||
</Card>
|
/>
|
||||||
<Card className="w-full h-fit mt-4">
|
</CardContent>
|
||||||
<CardHeader>
|
</Card>
|
||||||
<CardDescription>Run outputs</CardDescription>
|
<Card className="w-full h-fit mt-4">
|
||||||
</CardHeader>
|
<CardHeader>
|
||||||
|
<CardDescription>Run outputs</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<PublicRunOutputs preview={sharedDeployment.showcase_media} />
|
<PublicRunOutputs preview={sharedDeployment.showcase_media} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { setInitialUserData } from "../../../lib/setInitialUserData";
|
||||||
|
import { auth } from "@clerk/nextjs";
|
||||||
|
import { clerkClient } from "@clerk/nextjs/server";
|
||||||
|
import { CheckpointList } from "@/components/CheckpointList"
|
||||||
|
import { getAllUserCheckpoints } from "@/server/getAllUserCheckpoints";
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return <CheckpointListServer />;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function CheckpointListServer() {
|
||||||
|
const { userId } = auth();
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return <div>No auth</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await clerkClient.users.getUser(userId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
await setInitialUserData(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkpoints = await getAllUserCheckpoints()
|
||||||
|
|
||||||
|
if (!checkpoints) {
|
||||||
|
return <div>No checkpoints found</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<CheckpointList data={checkpoints}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import { CreateShareButton } from "@/components/CreateShareButton";
|
|
||||||
import { MachinesWSMain } from "@/components/MachinesWS";
|
import { MachinesWSMain } from "@/components/MachinesWS";
|
||||||
import { VersionDetails } from "@/components/VersionDetails";
|
import { VersionDetails } from "@/components/VersionDetails";
|
||||||
import {
|
import {
|
||||||
CopyWorkflowVersion,
|
CopyWorkflowVersion,
|
||||||
CreateDeploymentButton,
|
CreateDeploymentButton,
|
||||||
MachineSelect,
|
CreateShareButton,
|
||||||
RunWorkflowButton,
|
MachineSelect,
|
||||||
VersionSelect,
|
RunWorkflowButton,
|
||||||
ViewWorkflowDetailsButton,
|
VersionSelect,
|
||||||
|
ViewWorkflowDetailsButton,
|
||||||
} from "@/components/VersionSelect";
|
} from "@/components/VersionSelect";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { LoadingIcon } from "@/components/LoadingIcon";
|
|||||||
import { callServerPromise } from "@/components/callServerPromise";
|
import { callServerPromise } from "@/components/callServerPromise";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { useAuth, useClerk } from "@clerk/nextjs";
|
import { useAuth, useClerk } from "@clerk/nextjs";
|
||||||
import { MoreVertical } from "lucide-react";
|
import { MoreVertical } from "lucide-react";
|
||||||
@@ -15,80 +15,74 @@ import { useRouter } from "next/navigation";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export function ButtonAction({
|
export function ButtonAction({
|
||||||
action,
|
action,
|
||||||
children,
|
children,
|
||||||
routerAction = "back",
|
...rest
|
||||||
...rest
|
|
||||||
}: {
|
}: {
|
||||||
action: () => Promise<any>;
|
action: () => Promise<any>;
|
||||||
routerAction?: "refresh" | "back";
|
children: React.ReactNode;
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
}) {
|
||||||
const [pending, setPending] = useState(false);
|
const [pending, setPending] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (pending) return;
|
if (pending) return;
|
||||||
|
|
||||||
setPending(true);
|
setPending(true);
|
||||||
await callServerPromise(action());
|
await callServerPromise(action());
|
||||||
setPending(false);
|
setPending(false);
|
||||||
|
|
||||||
if (routerAction === "back") {
|
router.refresh();
|
||||||
router.back();
|
}}
|
||||||
router.refresh();
|
{...rest}
|
||||||
}
|
>
|
||||||
else if (routerAction === "refresh") router.refresh();
|
{children} {pending && <LoadingIcon />}
|
||||||
}}
|
</button>
|
||||||
{...rest}
|
);
|
||||||
>
|
|
||||||
{children} {pending && <LoadingIcon />}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ButtonActionMenu(props: {
|
export function ButtonActionMenu(props: {
|
||||||
title?: string;
|
title?: string;
|
||||||
actions: {
|
actions: {
|
||||||
title: string;
|
title: string;
|
||||||
action: () => Promise<any>;
|
action: () => Promise<any>;
|
||||||
}[];
|
}[];
|
||||||
}) {
|
}) {
|
||||||
const user = useAuth();
|
const user = useAuth();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const clerk = useClerk();
|
const clerk = useClerk();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button className="gap-2" variant="outline" disabled={isLoading}>
|
<Button className="gap-2" variant="outline" disabled={isLoading}>
|
||||||
{props.title}
|
{props.title}
|
||||||
{isLoading ? <LoadingIcon /> : <MoreVertical size={14} />}
|
{isLoading ? <LoadingIcon /> : <MoreVertical size={14} />}
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-56">
|
<DropdownMenuContent className="w-56">
|
||||||
{props.actions.map((action) => (
|
{props.actions.map((action) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={action.title}
|
key={action.title}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!user.isSignedIn) {
|
if (!user.isSignedIn) {
|
||||||
clerk.openSignIn({
|
clerk.openSignIn({
|
||||||
redirectUrl: window.location.href,
|
redirectUrl: window.location.href,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await callServerPromise(action.action());
|
await callServerPromise(action.action());
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{action.title}
|
{action.title}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
))}
|
))}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,315 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { getRelativeTime } from "../lib/getRelativeTime";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { InsertModal, UpdateModal } from "./InsertModal";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import type { getAllUserCheckpoints } from "@/server/getAllUserCheckpoints";
|
||||||
|
import type {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import {
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { insertCivitaiCheckpointSchema } from "@/db/schema";
|
||||||
|
import { addCivitaiCheckpoint } from "@/server/curdCheckpoint";
|
||||||
|
import { addCivitaiCheckpointSchema } from "@/server/addCheckpointSchema";
|
||||||
|
|
||||||
|
export type CheckpointItemList = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof getAllUserCheckpoints>>
|
||||||
|
>[0];
|
||||||
|
|
||||||
|
export const columns: ColumnDef<CheckpointItemList>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "id",
|
||||||
|
id: "select",
|
||||||
|
header: ({ table }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={table.getIsAllPageRowsSelected() ||
|
||||||
|
(table.getIsSomePageRowsSelected() && "indeterminate")}
|
||||||
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
|
aria-label="Select all"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
aria-label="Select row"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="flex items-center hover:underline"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const checkpoint = row.original;
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className="hover:underline flex gap-2"
|
||||||
|
href={`/storage/${checkpoint.id}`} // TODO
|
||||||
|
>
|
||||||
|
<span className="truncate max-w-[200px]">{row.original.model_name}</span>
|
||||||
|
|
||||||
|
<Badge variant="default">{}</Badge>
|
||||||
|
{checkpoint.is_public
|
||||||
|
? <Badge variant="success">Public</Badge>
|
||||||
|
: <Badge variant="teal">Private</Badge>}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "creator",
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="flex items-center hover:underline"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
Creator
|
||||||
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ row }) => {
|
||||||
|
// return <Badge variant="cyan">{row?.original?.user?.name ? row.original.user.name : "Public"}</Badge>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "date",
|
||||||
|
sortingFn: "datetime",
|
||||||
|
enableSorting: true,
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="w-full flex items-center justify-end hover:underline truncate"
|
||||||
|
// variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
Update Date
|
||||||
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="w-full capitalize text-right truncate">
|
||||||
|
{getRelativeTime(row.original.updated_at)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// id: "actions",
|
||||||
|
// enableHiding: false,
|
||||||
|
// cell: ({ row }) => {
|
||||||
|
// const checkpoint = row.original;
|
||||||
|
//
|
||||||
|
// return (
|
||||||
|
// <DropdownMenu>
|
||||||
|
// <DropdownMenuTrigger asChild>
|
||||||
|
// <Button variant="ghost" className="h-8 w-8 p-0">
|
||||||
|
// <span className="sr-only">Open menu</span>
|
||||||
|
// <MoreHorizontal className="h-4 w-4" />
|
||||||
|
// </Button>
|
||||||
|
// </DropdownMenuTrigger>
|
||||||
|
// <DropdownMenuContent align="end">
|
||||||
|
// <DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
|
// <DropdownMenuItem
|
||||||
|
// className="text-destructive"
|
||||||
|
// onClick={() => {
|
||||||
|
// deleteWorkflow(checkpoint.id);
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// Delete Workflow
|
||||||
|
// </DropdownMenuItem>
|
||||||
|
// </DropdownMenuContent>
|
||||||
|
// </DropdownMenu>
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function CheckpointList({ data }: { data: CheckpointItemList[] }) {
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] = React.useState<
|
||||||
|
VisibilityState
|
||||||
|
>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-rows-[auto,1fr,auto] h-full">
|
||||||
|
<div className="flex flex-row w-full items-center py-4">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter workflows..."
|
||||||
|
value={(table.getColumn("name")?.getFilterValue() as string) ?? ""}
|
||||||
|
onChange={(event) =>
|
||||||
|
table.getColumn("name")?.setFilterValue(event.target.value)}
|
||||||
|
className="max-w-sm"
|
||||||
|
/>
|
||||||
|
<div className="ml-auto flex gap-2">
|
||||||
|
<InsertModal
|
||||||
|
dialogClassName="sm:max-w-[600px]"
|
||||||
|
disabled={
|
||||||
|
false
|
||||||
|
// TODO: limitations based on plan
|
||||||
|
}
|
||||||
|
tooltip={"Add models using their civitai url!"}
|
||||||
|
title="Civitai Checkpoint"
|
||||||
|
description="Pick a model from civitai"
|
||||||
|
serverAction={addCivitaiCheckpoint}
|
||||||
|
formSchema={addCivitaiCheckpointSchema}
|
||||||
|
fieldConfig={{
|
||||||
|
civitai_url: {
|
||||||
|
fieldType: "fallback",
|
||||||
|
// fieldType: "fallback",
|
||||||
|
inputProps: { required: true },
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
Pick a checkpoint from{" "}
|
||||||
|
<a
|
||||||
|
href="https://www.civitai.com/models"
|
||||||
|
target="_blank"
|
||||||
|
className="underline text-blue-600 hover:text-blue-800 visited:text-purple-600"
|
||||||
|
>
|
||||||
|
civitai.com
|
||||||
|
</a>{" "}
|
||||||
|
and place it's url here
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ScrollArea className="h-full w-full rounded-md border">
|
||||||
|
<Table>
|
||||||
|
<TableHeader className="bg-background top-0 sticky">
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder ? null : flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext(),
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length
|
||||||
|
? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext(),
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</ScrollArea>
|
||||||
|
<div className="flex flex-row items-center justify-end space-x-2 py-4">
|
||||||
|
<div className="flex-1 text-sm text-muted-foreground">
|
||||||
|
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||||
|
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||||
|
</div>
|
||||||
|
<div className="space-x-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
"use client";
|
|
||||||
import { LoadingIcon } from "@/components/LoadingIcon";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { createDeployments } from "@/server/curdDeploments";
|
|
||||||
import type { getMachines } from "@/server/curdMachine";
|
|
||||||
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
|
||||||
import { Share } from "lucide-react";
|
|
||||||
import { parseAsInteger, useQueryState } from "next-usequerystate";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { useSelectedMachine } from "./VersionSelect";
|
|
||||||
import { callServerPromise } from "./callServerPromise";
|
|
||||||
|
|
||||||
export function CreateShareButton({
|
|
||||||
workflow,
|
|
||||||
machines,
|
|
||||||
}: {
|
|
||||||
workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>;
|
|
||||||
machines: Awaited<ReturnType<typeof getMachines>>;
|
|
||||||
}) {
|
|
||||||
const [version] = useQueryState("version", {
|
|
||||||
defaultValue: workflow?.versions[0].version ?? 1,
|
|
||||||
...parseAsInteger,
|
|
||||||
});
|
|
||||||
const [machine] = useSelectedMachine(machines);
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const workflow_version_id = workflow?.versions.find(
|
|
||||||
(x) => x.version === version,
|
|
||||||
)?.id;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button className="gap-2" disabled={isLoading} variant="outline">
|
|
||||||
Share {isLoading ? <LoadingIcon /> : <Share size={14} />}
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="w-56">
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={async () => {
|
|
||||||
if (!workflow_version_id) return;
|
|
||||||
|
|
||||||
setIsLoading(true);
|
|
||||||
await callServerPromise(
|
|
||||||
createDeployments(
|
|
||||||
workflow.id,
|
|
||||||
workflow_version_id,
|
|
||||||
machine,
|
|
||||||
"public-share",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setIsLoading(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Public
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
|
import { DeploymentRow, SharePageDeploymentRow } from "./DeploymentRow";
|
||||||
import { CodeBlock } from "@/components/CodeBlock";
|
import { CodeBlock } from "@/components/CodeBlock";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { TableRow } from "@/components/ui/table";
|
import { TableRow } from "@/components/ui/table";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow";
|
import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow";
|
||||||
import type { findAllDeployments } from "@/server/findAllRuns";
|
import type { findAllDeployments } from "@/server/findAllRuns";
|
||||||
import { DeploymentRow, SharePageDeploymentRow } from "./DeploymentRow";
|
|
||||||
|
|
||||||
const curlTemplate = `
|
const curlTemplate = `
|
||||||
curl --request POST \
|
curl --request POST \
|
||||||
@@ -83,145 +83,144 @@ const run = await client.getRun(run_id);
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function DeploymentDisplay({
|
export function DeploymentDisplay({
|
||||||
deployment,
|
deployment,
|
||||||
domain,
|
domain,
|
||||||
}: {
|
}: {
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
||||||
domain: string;
|
domain: string;
|
||||||
}) {
|
}) {
|
||||||
const workflowInput = getInputsFromWorkflow(deployment.version);
|
const workflowInput = getInputsFromWorkflow(deployment.version);
|
||||||
|
|
||||||
if (deployment.environment === "public-share") {
|
if (deployment.environment == "public-share") {
|
||||||
return <SharePageDeploymentRow deployment={deployment} />;
|
return <SharePageDeploymentRow deployment={deployment} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<DeploymentRow deployment={deployment} />
|
<DeploymentRow deployment={deployment} />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-3xl">
|
<DialogContent className="max-w-3xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="capitalize">
|
<DialogTitle className="capitalize">
|
||||||
{deployment.environment} Deployment
|
{deployment.environment} Deployment
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>Code for your deployment client</DialogDescription>
|
<DialogDescription>Code for your deployment client</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<ScrollArea className="max-h-[600px] pr-4">
|
<ScrollArea className="max-h-[600px] pr-4">
|
||||||
<Tabs defaultValue="client" className="w-full gap-2 text-sm">
|
<Tabs defaultValue="client" className="w-full gap-2 text-sm">
|
||||||
<TabsList className="grid w-fit grid-cols-3 mb-2">
|
<TabsList className="grid w-fit grid-cols-3 mb-2">
|
||||||
<TabsTrigger value="client">Server Client</TabsTrigger>
|
<TabsTrigger value="client">Server Client</TabsTrigger>
|
||||||
<TabsTrigger value="js">NodeJS Fetch</TabsTrigger>
|
<TabsTrigger value="js">NodeJS Fetch</TabsTrigger>
|
||||||
<TabsTrigger value="curl">CURL</TabsTrigger>
|
<TabsTrigger value="curl">CURL</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent className="flex flex-col gap-2 !mt-0" value="client">
|
<TabsContent className="flex flex-col gap-2 !mt-0" value="client">
|
||||||
<div>
|
<div>
|
||||||
Copy and paste the ComfyDeployClient form
|
Copy and paste the ComfyDeployClient form
|
||||||
<a
|
<a
|
||||||
href="https://github.com/BennyKok/comfyui-deploy-next-example/blob/main/src/lib/comfy-deploy.ts"
|
href="https://github.com/BennyKok/comfyui-deploy-next-example/blob/main/src/lib/comfy-deploy.ts"
|
||||||
className="text-blue-500 hover:underline"
|
className="text-blue-500 hover:underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
>
|
||||||
>
|
here
|
||||||
here
|
</a>
|
||||||
</a>
|
</div>
|
||||||
</div>
|
<CodeBlock
|
||||||
<CodeBlock
|
lang="js"
|
||||||
lang="js"
|
code={formatCode(
|
||||||
code={formatCode(
|
domain == "https://www.comfydeploy.com"
|
||||||
domain == "https://www.comfydeploy.com"
|
? jsClientSetupTemplateHostedVersion
|
||||||
? jsClientSetupTemplateHostedVersion
|
: jsClientSetupTemplate,
|
||||||
: jsClientSetupTemplate,
|
deployment,
|
||||||
deployment,
|
domain,
|
||||||
domain,
|
workflowInput
|
||||||
workflowInput,
|
)}
|
||||||
)}
|
/>
|
||||||
/>
|
Create a run via deployment id
|
||||||
Create a run via deployment id
|
<CodeBlock
|
||||||
<CodeBlock
|
lang="js"
|
||||||
lang="js"
|
code={formatCode(
|
||||||
code={formatCode(
|
workflowInput && workflowInput.length > 0
|
||||||
workflowInput && workflowInput.length > 0
|
? jsClientCreateRunTemplate
|
||||||
? jsClientCreateRunTemplate
|
: jsClientCreateRunNoInputsTemplate,
|
||||||
: jsClientCreateRunNoInputsTemplate,
|
deployment,
|
||||||
deployment,
|
domain,
|
||||||
domain,
|
workflowInput
|
||||||
workflowInput,
|
)}
|
||||||
)}
|
/>
|
||||||
/>
|
Check the status of the run, and retrieve the outputs
|
||||||
Check the status of the run, and retrieve the outputs
|
<CodeBlock
|
||||||
<CodeBlock
|
lang="js"
|
||||||
lang="js"
|
code={formatCode(
|
||||||
code={formatCode(
|
clientTemplate_checkStatus,
|
||||||
clientTemplate_checkStatus,
|
deployment,
|
||||||
deployment,
|
domain
|
||||||
domain,
|
)}
|
||||||
)}
|
/>
|
||||||
/>
|
</TabsContent>
|
||||||
</TabsContent>
|
<TabsContent className="flex flex-col gap-2 !mt-0" value="js">
|
||||||
<TabsContent className="flex flex-col gap-2 !mt-0" value="js">
|
Trigger the workflow
|
||||||
Trigger the workflow
|
<CodeBlock
|
||||||
<CodeBlock
|
lang="js"
|
||||||
lang="js"
|
code={formatCode(jsTemplate, deployment, domain, workflowInput)}
|
||||||
code={formatCode(jsTemplate, deployment, domain, workflowInput)}
|
/>
|
||||||
/>
|
Check the status of the run, and retrieve the outputs
|
||||||
Check the status of the run, and retrieve the outputs
|
<CodeBlock
|
||||||
<CodeBlock
|
lang="js"
|
||||||
lang="js"
|
code={formatCode(jsTemplate_checkStatus, deployment, domain)}
|
||||||
code={formatCode(jsTemplate_checkStatus, deployment, domain)}
|
/>
|
||||||
/>
|
</TabsContent>
|
||||||
</TabsContent>
|
<TabsContent className="flex flex-col gap-2 !mt-2" value="curl">
|
||||||
<TabsContent className="flex flex-col gap-2 !mt-2" value="curl">
|
<CodeBlock
|
||||||
<CodeBlock
|
lang="bash"
|
||||||
lang="bash"
|
code={formatCode(curlTemplate, deployment, domain)}
|
||||||
code={formatCode(curlTemplate, deployment, domain)}
|
/>
|
||||||
/>
|
<CodeBlock
|
||||||
<CodeBlock
|
lang="bash"
|
||||||
lang="bash"
|
code={formatCode(curlTemplate_checkStatus, deployment, domain)}
|
||||||
code={formatCode(curlTemplate_checkStatus, deployment, domain)}
|
/>
|
||||||
/>
|
</TabsContent>
|
||||||
</TabsContent>
|
</Tabs>
|
||||||
</Tabs>
|
</ScrollArea>
|
||||||
</ScrollArea>
|
</DialogContent>
|
||||||
</DialogContent>
|
</Dialog>
|
||||||
</Dialog>
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCode(
|
function formatCode(
|
||||||
codeTemplate: string,
|
codeTemplate: string,
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0],
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0],
|
||||||
domain: string,
|
domain: string,
|
||||||
inputs?: ReturnType<typeof getInputsFromWorkflow>,
|
inputs?: ReturnType<typeof getInputsFromWorkflow>,
|
||||||
inputsTabs?: number,
|
inputsTabs?: number
|
||||||
) {
|
) {
|
||||||
if (inputs && inputs.length > 0) {
|
if (inputs && inputs.length > 0) {
|
||||||
codeTemplate = codeTemplate.replace(
|
codeTemplate = codeTemplate.replace(
|
||||||
"inputs: {}",
|
"inputs: {}",
|
||||||
`inputs: ${JSON.stringify(
|
`inputs: ${JSON.stringify(
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
inputs.map((x) => {
|
inputs.map((x) => {
|
||||||
return [x?.input_id, ""];
|
return [x?.input_id, ""];
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
2,
|
2
|
||||||
)
|
)
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map((line, index) => (index === 0 ? line : ` ${line}`)) // Add two spaces indentation except for the first line
|
.map((line, index) => (index === 0 ? line : ` ${line}`)) // Add two spaces indentation except for the first line
|
||||||
.join("\n")}`,
|
.join("\n")}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
codeTemplate = codeTemplate.replace(
|
codeTemplate = codeTemplate.replace(
|
||||||
`
|
`
|
||||||
inputs: {}`,
|
inputs: {}`,
|
||||||
"",
|
""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return codeTemplate
|
return codeTemplate
|
||||||
.replace("<URL>", `${domain ?? "http://localhost:3000"}/api/run`)
|
.replace("<URL>", `${domain ?? "http://localhost:3000"}/api/run`)
|
||||||
.replace("<ID>", deployment.id)
|
.replace("<ID>", deployment.id)
|
||||||
.replace("<URLONLY>", domain ?? "http://localhost:3000");
|
.replace("<URLONLY>", domain ?? "http://localhost:3000");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,57 +6,55 @@ import type { findAllDeployments } from "@/server/findAllRuns";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
export function SharePageDeploymentRow({
|
export function SharePageDeploymentRow({
|
||||||
deployment,
|
deployment,
|
||||||
}: {
|
}: {
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
className="appearance-none hover:cursor-pointer"
|
className="appearance-none hover:cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (deployment.environment === "public-share") {
|
if (deployment.environment == "public-share") {
|
||||||
router.push(
|
router.push(`/share/${deployment.id}/settings`);
|
||||||
`/share/${deployment.share_slug ?? deployment.id}/settings`,
|
}
|
||||||
);
|
}}
|
||||||
}
|
>
|
||||||
}}
|
<TableCell className="capitalize truncate">
|
||||||
>
|
{deployment.environment}
|
||||||
<TableCell className="capitalize truncate">
|
</TableCell>
|
||||||
{deployment.environment}
|
<TableCell className="font-medium truncate">
|
||||||
</TableCell>
|
{deployment.version?.version}
|
||||||
<TableCell className="font-medium truncate">
|
</TableCell>
|
||||||
{deployment.version?.version}
|
<TableCell className="font-medium truncate">
|
||||||
</TableCell>
|
{deployment.machine?.name}
|
||||||
<TableCell className="font-medium truncate">
|
</TableCell>
|
||||||
{deployment.machine?.name}
|
<TableCell className="text-right truncate">
|
||||||
</TableCell>
|
{getRelativeTime(deployment.updated_at)}
|
||||||
<TableCell className="text-right truncate">
|
</TableCell>
|
||||||
{getRelativeTime(deployment.updated_at)}
|
</TableRow>
|
||||||
</TableCell>
|
);
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DeploymentRow({
|
export function DeploymentRow({
|
||||||
deployment,
|
deployment,
|
||||||
}: {
|
}: {
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableCell className="capitalize truncate">
|
<TableCell className="capitalize truncate">
|
||||||
{deployment.environment}
|
{deployment.environment}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="font-medium truncate">
|
<TableCell className="font-medium truncate">
|
||||||
{deployment.version?.version}
|
{deployment.version?.version}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="font-medium truncate">
|
<TableCell className="font-medium truncate">
|
||||||
{deployment.machine?.name}
|
{deployment.machine?.name}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right truncate">
|
<TableCell className="text-right truncate">
|
||||||
{getRelativeTime(deployment.updated_at)}
|
{getRelativeTime(deployment.updated_at)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
href={machine.endpoint.replace(
|
href={machine.endpoint.replace(
|
||||||
"comfyui-api",
|
"comfyui-api",
|
||||||
"comfyui-app"
|
"comfyui-app"
|
||||||
)} rel="noreferrer"
|
)}
|
||||||
>
|
>
|
||||||
Open ComfyUI
|
Open ComfyUI
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
} from "@clerk/nextjs";
|
} from "@clerk/nextjs";
|
||||||
import { Github, Menu } from "lucide-react";
|
import { Github, Menu } from "lucide-react";
|
||||||
import meta from "next-gen/config";
|
import meta from "next-gen/config";
|
||||||
import { useFeatureFlagEnabled } from "posthog-js/react";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useMediaQuery } from "usehooks-ts";
|
import { useMediaQuery } from "usehooks-ts";
|
||||||
|
|
||||||
@@ -30,13 +29,9 @@ export function Navbar() {
|
|||||||
const { organization } = useOrganization();
|
const { organization } = useOrganization();
|
||||||
const _isDesktop = useMediaQuery("(min-width: 1024px)");
|
const _isDesktop = useMediaQuery("(min-width: 1024px)");
|
||||||
const [isDesktop, setIsDesktop] = useState(true);
|
const [isDesktop, setIsDesktop] = useState(true);
|
||||||
|
|
||||||
const pricingPlanFlagEnable = useFeatureFlagEnabled("pricing-plan");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsDesktop(_isDesktop);
|
setIsDesktop(_isDesktop);
|
||||||
}, [_isDesktop]);
|
}, [_isDesktop]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row items-center gap-4">
|
<div className="flex flex-row items-center gap-4">
|
||||||
@@ -90,15 +85,6 @@ export function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
{isDesktop && <NavbarMenu />}
|
{isDesktop && <NavbarMenu />}
|
||||||
{pricingPlanFlagEnable && (
|
|
||||||
<Button
|
|
||||||
asChild
|
|
||||||
variant="link"
|
|
||||||
className="rounded-full aspect-square p-2 mr-4"
|
|
||||||
>
|
|
||||||
<a href="/pricing">Pricing</a>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
variant="link"
|
variant="link"
|
||||||
@@ -112,11 +98,7 @@ export function Navbar() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
className="rounded-full aspect-square p-2"
|
className="rounded-full aspect-square p-2"
|
||||||
>
|
>
|
||||||
<a
|
<a target="_blank" href="https://github.com/BennyKok/comfyui-deploy">
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/BennyKok/comfyui-deploy"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
<Github />
|
<Github />
|
||||||
</a>
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ export function NavbarMenu({ className }: { className?: string }) {
|
|||||||
name: "API Keys",
|
name: "API Keys",
|
||||||
path: "/api-keys",
|
path: "/api-keys",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Storage",
|
||||||
|
path: "/storage",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -42,9 +46,9 @@ export function NavbarMenu({ className }: { className?: string }) {
|
|||||||
{isDesktop && (
|
{isDesktop && (
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue={pathname}
|
defaultValue={pathname}
|
||||||
className="w-[300px] flex pointer-events-auto"
|
className="w-[400px] flex pointer-events-auto"
|
||||||
>
|
>
|
||||||
<TabsList className="grid w-full grid-cols-3">
|
<TabsList className="grid w-full grid-cols-4">
|
||||||
{pages.map((page) => (
|
{pages.map((page) => (
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
key={page.name}
|
key={page.name}
|
||||||
|
|||||||
@@ -1,74 +1,63 @@
|
|||||||
|
import { LiveStatus } from "./LiveStatus";
|
||||||
import { RunInputs } from "@/components/RunInputs";
|
import { RunInputs } from "@/components/RunInputs";
|
||||||
import { RunOutputs } from "@/components/RunOutputs";
|
import { RunOutputs } from "@/components/RunOutputs";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { TableCell, TableRow } from "@/components/ui/table";
|
import { TableCell, TableRow } from "@/components/ui/table";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import { getDuration, getRelativeTime } from "@/lib/getRelativeTime";
|
import { getDuration, getRelativeTime } from "@/lib/getRelativeTime";
|
||||||
import { type findAllRuns } from "@/server/findAllRuns";
|
import { type findAllRuns } from "@/server/findAllRuns";
|
||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
import { LiveStatus } from "./LiveStatus";
|
|
||||||
|
|
||||||
export async function RunDisplay({
|
export async function RunDisplay({
|
||||||
run,
|
run,
|
||||||
}: {
|
}: {
|
||||||
run: Awaited<ReturnType<typeof findAllRuns>>[0];
|
run: Awaited<ReturnType<typeof findAllRuns>>[0];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>{run.number}</TableCell>
|
<TableCell>{run.number}</TableCell>
|
||||||
<TableCell className="font-medium truncate">
|
<TableCell className="font-medium truncate">
|
||||||
{run.machine?.name}
|
{run.machine?.name}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="truncate">
|
<TableCell className="truncate">
|
||||||
{getRelativeTime(run.created_at)}
|
{getRelativeTime(run.created_at)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{run.version?.version}</TableCell>
|
<TableCell>{run.version?.version}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Badge variant="outline" className="truncate">
|
<Badge variant="outline" className="truncate">
|
||||||
{run.origin}
|
{run.origin}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="truncate">
|
<TableCell className="truncate">
|
||||||
<Tooltip>
|
{getDuration(run.duration)}
|
||||||
<TooltipTrigger>{getDuration(run.duration)}</TooltipTrigger>
|
</TableCell>
|
||||||
<TooltipContent>
|
<LiveStatus run={run} />
|
||||||
<div>Cold start: {getDuration(run.cold_start_duration)}</div>
|
</TableRow>
|
||||||
<div>Run duration: {getDuration(run.run_duration)}</div>
|
</DialogTrigger>
|
||||||
</TooltipContent>
|
<DialogContent className="max-w-3xl">
|
||||||
</Tooltip>
|
<DialogHeader>
|
||||||
</TableCell>
|
<DialogTitle>Run outputs</DialogTitle>
|
||||||
<LiveStatus run={run} />
|
<DialogDescription>
|
||||||
</TableRow>
|
You can view your run's outputs here
|
||||||
</DialogTrigger>
|
</DialogDescription>
|
||||||
<DialogContent className="max-w-3xl">
|
</DialogHeader>
|
||||||
<DialogHeader>
|
<div className="max-h-96 overflow-y-scroll">
|
||||||
<DialogTitle>Run outputs</DialogTitle>
|
<RunInputs run={run} />
|
||||||
<DialogDescription>
|
<Suspense>
|
||||||
You can view your run's outputs here
|
<RunOutputs run_id={run.id} />
|
||||||
</DialogDescription>
|
</Suspense>
|
||||||
</DialogHeader>
|
</div>
|
||||||
<div className="max-h-96 overflow-y-scroll">
|
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}
|
||||||
<RunInputs run={run} />
|
</DialogContent>
|
||||||
<Suspense>
|
</Dialog>
|
||||||
<RunOutputs run_id={run.id} />
|
);
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
import {
|
||||||
|
findAllDeployments,
|
||||||
|
findAllRunsWithCounts,
|
||||||
|
} from "../server/findAllRuns";
|
||||||
|
import { DeploymentDisplay } from "./DeploymentDisplay";
|
||||||
|
import { PaginationControl } from "./PaginationControl";
|
||||||
|
import { RunDisplay } from "./RunDisplay";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -8,13 +15,6 @@ import {
|
|||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { parseAsInteger } from "next-usequerystate";
|
import { parseAsInteger } from "next-usequerystate";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import {
|
|
||||||
findAllDeployments,
|
|
||||||
findAllRunsWithCounts,
|
|
||||||
} from "../server/findAllRuns";
|
|
||||||
import { DeploymentDisplay } from "./DeploymentDisplay";
|
|
||||||
import { PaginationControl } from "./PaginationControl";
|
|
||||||
import { RunDisplay } from "./RunDisplay";
|
|
||||||
|
|
||||||
const itemPerPage = 6;
|
const itemPerPage = 6;
|
||||||
const pageParser = parseAsInteger.withDefault(1);
|
const pageParser = parseAsInteger.withDefault(1);
|
||||||
@@ -33,40 +33,40 @@ export async function RunsTable(props: {
|
|||||||
offset: (page - 1) * itemPerPage,
|
offset: (page - 1) * itemPerPage,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="overflow-auto h-fit w-full">
|
<div className="overflow-auto h-fit w-full">
|
||||||
<Table className="">
|
<Table className="">
|
||||||
{allRuns.length === 0 && (
|
{allRuns.length == 0 && (
|
||||||
<TableCaption>A list of your recent runs.</TableCaption>
|
<TableCaption>A list of your recent runs.</TableCaption>
|
||||||
)}
|
)}
|
||||||
<TableHeader className="bg-background top-0 sticky">
|
<TableHeader className="bg-background top-0 sticky">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="truncate">Number</TableHead>
|
<TableHead className="truncate">Number</TableHead>
|
||||||
<TableHead className="truncate">Machine</TableHead>
|
<TableHead className="truncate">Machine</TableHead>
|
||||||
<TableHead className="truncate">Time</TableHead>
|
<TableHead className="truncate">Time</TableHead>
|
||||||
<TableHead className="truncate">Version</TableHead>
|
<TableHead className="truncate">Version</TableHead>
|
||||||
<TableHead className="truncate">Origin</TableHead>
|
<TableHead className="truncate">Origin</TableHead>
|
||||||
<TableHead className="truncate">Duration</TableHead>
|
<TableHead className="truncate">Duration</TableHead>
|
||||||
<TableHead className="truncate">Live Status</TableHead>
|
<TableHead className="truncate">Live Status</TableHead>
|
||||||
<TableHead className="text-right">Status</TableHead>
|
<TableHead className="text-right">Status</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{allRuns.map((run) => (
|
{allRuns.map((run) => (
|
||||||
<RunDisplay run={run} key={run.id} />
|
<RunDisplay run={run} key={run.id} />
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{Math.ceil(total / itemPerPage) > 0 && (
|
{Math.ceil(total / itemPerPage) > 0 && (
|
||||||
<PaginationControl
|
<PaginationControl
|
||||||
totalPage={Math.ceil(total / itemPerPage)}
|
totalPage={Math.ceil(total / itemPerPage)}
|
||||||
currentPage={page}
|
currentPage={page}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function DeploymentsTable(props: { workflow_id: string }) {
|
export async function DeploymentsTable(props: { workflow_id: string }) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useServerActionData } from "./useServerActionData";
|
||||||
import { ButtonAction } from "@/components/ButtonActionLoader";
|
import { ButtonAction } from "@/components/ButtonActionLoader";
|
||||||
import { UpdateModal } from "@/components/InsertModal";
|
import { UpdateModal } from "@/components/InsertModal";
|
||||||
import { LoadingPageWrapper } from "@/components/LoadingWrapper";
|
import { LoadingPageWrapper } from "@/components/LoadingWrapper";
|
||||||
@@ -14,7 +15,6 @@ import { ExternalLink } from "lucide-react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useServerActionData } from "./useServerActionData";
|
|
||||||
|
|
||||||
export function SharePageSettings({
|
export function SharePageSettings({
|
||||||
deployment_id,
|
deployment_id,
|
||||||
@@ -58,14 +58,13 @@ export function SharePageSettings({
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<ButtonAction
|
<ButtonAction
|
||||||
routerAction="back"
|
|
||||||
action={removePublicShareDeployment.bind(null, deployment.id)}
|
action={removePublicShareDeployment.bind(null, deployment.id)}
|
||||||
>
|
>
|
||||||
Remove
|
Remove
|
||||||
</ButtonAction>
|
</ButtonAction>
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild className="gap-2 truncate" type="button">
|
<Button asChild className="gap-2 truncate" type="button">
|
||||||
<Link href={`/share/${deployment.share_slug ?? deployment.id}`} target="_blank">
|
<Link href={`/share/${deployment.id}`} target="_blank">
|
||||||
View Share Page <ExternalLink size={14} />
|
View Share Page <ExternalLink size={14} />
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod";
|
||||||
|
import { callServerPromise } from "./callServerPromise";
|
||||||
|
import fetcher from "./fetcher";
|
||||||
import { LoadingIcon } from "@/components/LoadingIcon";
|
import { LoadingIcon } from "@/components/LoadingIcon";
|
||||||
import AutoForm, { AutoFormSubmit } from "@/components/ui/auto-form";
|
import AutoForm, { AutoFormSubmit } from "@/components/ui/auto-form";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
@@ -41,16 +44,20 @@ import { checkStatus, createRun } from "@/server/createRun";
|
|||||||
import { createDeployments } from "@/server/curdDeploments";
|
import { createDeployments } from "@/server/curdDeploments";
|
||||||
import type { getMachines } from "@/server/curdMachine";
|
import type { getMachines } from "@/server/curdMachine";
|
||||||
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
||||||
import { Copy, ExternalLink, Info, MoreVertical, Play } from "lucide-react";
|
import {
|
||||||
|
Copy,
|
||||||
|
ExternalLink,
|
||||||
|
Info,
|
||||||
|
MoreVertical,
|
||||||
|
Play,
|
||||||
|
Share,
|
||||||
|
} from "lucide-react";
|
||||||
import { parseAsInteger, useQueryState } from "next-usequerystate";
|
import { parseAsInteger, useQueryState } from "next-usequerystate";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod";
|
|
||||||
import { callServerPromise } from "./callServerPromise";
|
|
||||||
import fetcher from "./fetcher";
|
|
||||||
|
|
||||||
export function VersionSelect({
|
export function VersionSelect({
|
||||||
workflow,
|
workflow,
|
||||||
@@ -115,14 +122,12 @@ export function MachineSelect({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSelectedMachine(
|
function useSelectedMachine(machines: Awaited<ReturnType<typeof getMachines>>) {
|
||||||
machines: Awaited<ReturnType<typeof getMachines>>,
|
const a = useQueryState("machine", {
|
||||||
) {
|
defaultValue: machines?.[0]?.id ?? "",
|
||||||
const a = useQueryState("machine", {
|
});
|
||||||
defaultValue: machines?.[0]?.id ?? "",
|
|
||||||
});
|
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublicRunStore = {
|
type PublicRunStore = {
|
||||||
@@ -368,6 +373,55 @@ export function CreateDeploymentButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function CreateShareButton({
|
||||||
|
workflow,
|
||||||
|
machines,
|
||||||
|
}: {
|
||||||
|
workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>;
|
||||||
|
machines: Awaited<ReturnType<typeof getMachines>>;
|
||||||
|
}) {
|
||||||
|
const [version] = useQueryState("version", {
|
||||||
|
defaultValue: workflow?.versions[0].version ?? 1,
|
||||||
|
...parseAsInteger,
|
||||||
|
});
|
||||||
|
const [machine] = useSelectedMachine(machines);
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const workflow_version_id = workflow?.versions.find(
|
||||||
|
(x) => x.version === version
|
||||||
|
)?.id;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button className="gap-2" disabled={isLoading} variant="outline">
|
||||||
|
Share {isLoading ? <LoadingIcon /> : <Share size={14} />}
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={async () => {
|
||||||
|
if (!workflow_version_id) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
await callServerPromise(
|
||||||
|
createDeployments(
|
||||||
|
workflow.id,
|
||||||
|
workflow_version_id,
|
||||||
|
machine,
|
||||||
|
"public-share"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setIsLoading(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Public
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function CopyWorkflowVersion({
|
export function CopyWorkflowVersion({
|
||||||
workflow,
|
workflow,
|
||||||
}: {
|
}: {
|
||||||
@@ -544,7 +598,7 @@ export function ViewWorkflowDetailsButton({
|
|||||||
<a
|
<a
|
||||||
href={group.url}
|
href={group.url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="hover:underline" rel="noreferrer"
|
className="hover:underline"
|
||||||
>
|
>
|
||||||
{key}
|
{key}
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
|
|||||||
@@ -42,105 +42,105 @@ const Model = z.object({
|
|||||||
url: z.string(),
|
url: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CivitalModelSchema = z.object({
|
export const CivitaiModel = z.object({
|
||||||
items: z.array(
|
id: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
// poi: z.boolean(),
|
||||||
|
// nsfw: z.boolean(),
|
||||||
|
// allowNoCredit: z.boolean(),
|
||||||
|
// allowCommercialUse: z.string(),
|
||||||
|
// allowDerivatives: z.boolean(),
|
||||||
|
// allowDifferentLicense: z.boolean(),
|
||||||
|
// stats: z.object({
|
||||||
|
// downloadCount: z.number(),
|
||||||
|
// favoriteCount: z.number(),
|
||||||
|
// commentCount: z.number(),
|
||||||
|
// ratingCount: z.number(),
|
||||||
|
// rating: z.number(),
|
||||||
|
// tippedAmountCount: z.number(),
|
||||||
|
// }),
|
||||||
|
creator: z
|
||||||
|
.object({
|
||||||
|
username: z.string().nullable(),
|
||||||
|
image: z.string().nullable().default(null),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
tags: z.array(z.string()),
|
||||||
|
modelVersions: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
|
modelId: z.number(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
description: z.string(),
|
createdAt: z.string(),
|
||||||
type: z.string(),
|
updatedAt: z.string(),
|
||||||
// poi: z.boolean(),
|
status: z.string(),
|
||||||
// nsfw: z.boolean(),
|
publishedAt: z.string(),
|
||||||
// allowNoCredit: z.boolean(),
|
trainedWords: z.array(z.unknown()),
|
||||||
// allowCommercialUse: z.string(),
|
trainingStatus: z.string().nullable(),
|
||||||
// allowDerivatives: z.boolean(),
|
trainingDetails: z.string().nullable(),
|
||||||
// allowDifferentLicense: z.boolean(),
|
baseModel: z.string(),
|
||||||
// stats: z.object({
|
baseModelType: z.string().nullable(),
|
||||||
// downloadCount: z.number(),
|
earlyAccessTimeFrame: z.number(),
|
||||||
// favoriteCount: z.number(),
|
description: z.string().nullable(),
|
||||||
// commentCount: z.number(),
|
vaeId: z.number().nullable(),
|
||||||
// ratingCount: z.number(),
|
stats: z.object({
|
||||||
// rating: z.number(),
|
downloadCount: z.number(),
|
||||||
// tippedAmountCount: z.number(),
|
ratingCount: z.number(),
|
||||||
// }),
|
rating: z.number(),
|
||||||
creator: z
|
}),
|
||||||
.object({
|
files: z.array(
|
||||||
username: z.string().nullable(),
|
|
||||||
image: z.string().nullable().default(null),
|
|
||||||
})
|
|
||||||
.nullable(),
|
|
||||||
tags: z.array(z.string()),
|
|
||||||
modelVersions: z.array(
|
|
||||||
z.object({
|
z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
modelId: z.number(),
|
sizeKB: z.number(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
createdAt: z.string(),
|
type: z.string(),
|
||||||
updatedAt: z.string(),
|
// metadata: z.object({
|
||||||
status: z.string(),
|
// fp: z.string().nullable().optional(),
|
||||||
publishedAt: z.string(),
|
// size: z.string().nullable().optional(),
|
||||||
trainedWords: z.array(z.unknown()),
|
// format: z.string().nullable().optional(),
|
||||||
trainingStatus: z.string().nullable(),
|
// }),
|
||||||
trainingDetails: z.string().nullable(),
|
// pickleScanResult: z.string(),
|
||||||
baseModel: z.string(),
|
// pickleScanMessage: z.string(),
|
||||||
baseModelType: z.string().nullable(),
|
// virusScanResult: z.string(),
|
||||||
earlyAccessTimeFrame: z.number(),
|
// virusScanMessage: z.string().nullable(),
|
||||||
description: z.string().nullable(),
|
// scannedAt: z.string(),
|
||||||
vaeId: z.number().nullable(),
|
// hashes: z.object({
|
||||||
stats: z.object({
|
// AutoV1: z.string().nullable().optional(),
|
||||||
downloadCount: z.number(),
|
// AutoV2: z.string().nullable().optional(),
|
||||||
ratingCount: z.number(),
|
// SHA256: z.string().nullable().optional(),
|
||||||
rating: z.number(),
|
// CRC32: z.string().nullable().optional(),
|
||||||
}),
|
// BLAKE3: z.string().nullable().optional(),
|
||||||
files: z.array(
|
// }),
|
||||||
z.object({
|
|
||||||
id: z.number(),
|
|
||||||
sizeKB: z.number(),
|
|
||||||
name: z.string(),
|
|
||||||
type: z.string(),
|
|
||||||
// metadata: z.object({
|
|
||||||
// fp: z.string().nullable().optional(),
|
|
||||||
// size: z.string().nullable().optional(),
|
|
||||||
// format: z.string().nullable().optional(),
|
|
||||||
// }),
|
|
||||||
// pickleScanResult: z.string(),
|
|
||||||
// pickleScanMessage: z.string(),
|
|
||||||
// virusScanResult: z.string(),
|
|
||||||
// virusScanMessage: z.string().nullable(),
|
|
||||||
// scannedAt: z.string(),
|
|
||||||
// hashes: z.object({
|
|
||||||
// AutoV1: z.string().nullable().optional(),
|
|
||||||
// AutoV2: z.string().nullable().optional(),
|
|
||||||
// SHA256: z.string().nullable().optional(),
|
|
||||||
// CRC32: z.string().nullable().optional(),
|
|
||||||
// BLAKE3: z.string().nullable().optional(),
|
|
||||||
// }),
|
|
||||||
downloadUrl: z.string(),
|
|
||||||
// primary: z.boolean().default(false),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
images: z.array(
|
|
||||||
z.object({
|
|
||||||
id: z.number(),
|
|
||||||
url: z.string(),
|
|
||||||
nsfw: z.string(),
|
|
||||||
width: z.number(),
|
|
||||||
height: z.number(),
|
|
||||||
hash: z.string(),
|
|
||||||
type: z.string(),
|
|
||||||
metadata: z.object({
|
|
||||||
hash: z.string(),
|
|
||||||
width: z.number(),
|
|
||||||
height: z.number(),
|
|
||||||
}),
|
|
||||||
meta: z.any(),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
downloadUrl: z.string(),
|
downloadUrl: z.string(),
|
||||||
})
|
// primary: z.boolean().default(false),
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
})
|
images: z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.number(),
|
||||||
|
url: z.string(),
|
||||||
|
nsfw: z.string(),
|
||||||
|
width: z.number(),
|
||||||
|
height: z.number(),
|
||||||
|
hash: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
metadata: z.object({
|
||||||
|
hash: z.string(),
|
||||||
|
width: z.number(),
|
||||||
|
height: z.number(),
|
||||||
|
}),
|
||||||
|
meta: z.any(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
downloadUrl: z.string(),
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CivitalModelSchema = z.object({
|
||||||
|
items: z.array(CivitaiModel),
|
||||||
metadata: z.object({
|
metadata: z.object({
|
||||||
totalItems: z.number(),
|
totalItems: z.number(),
|
||||||
currentPage: z.number(),
|
currentPage: z.number(),
|
||||||
@@ -197,7 +197,7 @@ function mapType(type: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mapModelsList(
|
function mapModelsList(
|
||||||
models: z.infer<typeof CivitalModelSchema>
|
models: z.infer<typeof CivitalModelSchema>,
|
||||||
): z.infer<typeof ModelListWrapper> {
|
): z.infer<typeof ModelListWrapper> {
|
||||||
return {
|
return {
|
||||||
models: models.items.flatMap((item) => {
|
models: models.items.flatMap((item) => {
|
||||||
@@ -241,8 +241,9 @@ function getUrl(search?: string) {
|
|||||||
export function CivitaiModelRegistry({
|
export function CivitaiModelRegistry({
|
||||||
field,
|
field,
|
||||||
}: Pick<AutoFormInputComponentProps, "field">) {
|
}: Pick<AutoFormInputComponentProps, "field">) {
|
||||||
const [modelList, setModelList] =
|
const [modelList, setModelList] = React.useState<
|
||||||
React.useState<z.infer<typeof ModelListWrapper>>();
|
z.infer<typeof ModelListWrapper>
|
||||||
|
>();
|
||||||
|
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
|
||||||
@@ -301,8 +302,9 @@ export function CivitaiModelRegistry({
|
|||||||
export function ComfyUIManagerModelRegistry({
|
export function ComfyUIManagerModelRegistry({
|
||||||
field,
|
field,
|
||||||
}: Pick<AutoFormInputComponentProps, "field">) {
|
}: Pick<AutoFormInputComponentProps, "field">) {
|
||||||
const [modelList, setModelList] =
|
const [modelList, setModelList] = React.useState<
|
||||||
React.useState<z.infer<typeof ModelListWrapper>>();
|
z.infer<typeof ModelListWrapper>
|
||||||
|
>();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
@@ -310,7 +312,7 @@ export function ComfyUIManagerModelRegistry({
|
|||||||
"https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/model-list.json",
|
"https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/model-list.json",
|
||||||
{
|
{
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.then((x) => x.json())
|
.then((x) => x.json())
|
||||||
.then((a) => {
|
.then((a) => {
|
||||||
@@ -353,14 +355,14 @@ export function ModelSelector({
|
|||||||
if (
|
if (
|
||||||
prevSelectedModels.some(
|
prevSelectedModels.some(
|
||||||
(selectedModel) =>
|
(selectedModel) =>
|
||||||
selectedModel.url + selectedModel.name === model.url + model.name
|
selectedModel.url + selectedModel.name === model.url + model.name,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
field.onChange(
|
field.onChange(
|
||||||
prevSelectedModels.filter(
|
prevSelectedModels.filter(
|
||||||
(selectedModel) =>
|
(selectedModel) =>
|
||||||
selectedModel.url + selectedModel.name !== model.url + model.name
|
selectedModel.url + selectedModel.name !== model.url + model.name,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
field.onChange([...prevSelectedModels, model]);
|
field.onChange([...prevSelectedModels, model]);
|
||||||
@@ -408,10 +410,10 @@ export function ModelSelector({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"ml-auto h-4 w-4",
|
"ml-auto h-4 w-4",
|
||||||
value.some(
|
value.some(
|
||||||
(selectedModel) => selectedModel.url === model.url
|
(selectedModel) => selectedModel.url === model.url,
|
||||||
)
|
)
|
||||||
? "opacity-100"
|
? "opacity-100"
|
||||||
: "opacity-0"
|
: "opacity-0",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import type { AutoFormInputComponentProps } from "../ui/auto-form/types";
|
||||||
|
import { FormControl, FormItem, FormLabel } from "../ui/form";
|
||||||
|
import { LoadingIcon } from "@/components/LoadingIcon";
|
||||||
|
import * as React from "react";
|
||||||
|
import AutoFormInput from "../ui/auto-form/fields/input";
|
||||||
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
import { CivitaiModel } from "./ModelPickerView";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { insertCivitaiCheckpointSchema } from "@/db/schema";
|
||||||
|
|
||||||
|
function getUrl(civitai_url: string) {
|
||||||
|
// expect to be a URL to be https://civitai.com/models/36520
|
||||||
|
// possiblity with slugged name and query-param modelVersionId
|
||||||
|
|
||||||
|
const baseUrl = "https://civitai.com/api/v1/models/";
|
||||||
|
const url = new URL(civitai_url);
|
||||||
|
const pathSegments = url.pathname.split("/");
|
||||||
|
const modelId = pathSegments[pathSegments.indexOf("models") + 1];
|
||||||
|
const modelVersionId = url.searchParams.get("modelVersionId");
|
||||||
|
|
||||||
|
return { url: baseUrl + modelId, modelVersionId };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AutoFormCheckpointInput(
|
||||||
|
props: AutoFormInputComponentProps,
|
||||||
|
) {
|
||||||
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
const [modelRes, setModelRes] = React.useState<
|
||||||
|
z.infer<typeof CivitaiModel>
|
||||||
|
>();
|
||||||
|
const [modelVersionid, setModelVersionId] = React.useState<string | null>();
|
||||||
|
const { label, isRequired, fieldProps, zodItem, fieldConfigItem } = props;
|
||||||
|
|
||||||
|
const handleSearch = useDebouncedCallback((search) => {
|
||||||
|
const validationResult = insertCivitaiCheckpointSchema.shape.civitai_url
|
||||||
|
.safeParse(search);
|
||||||
|
if (!validationResult.success) {
|
||||||
|
console.error(validationResult.error);
|
||||||
|
// Optionally set an error state here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const { url, modelVersionId: versionId } = getUrl(search);
|
||||||
|
setModelVersionId(versionId);
|
||||||
|
fetch(url, {
|
||||||
|
signal: controller.signal,
|
||||||
|
})
|
||||||
|
.then((x) => x.json())
|
||||||
|
.then((a) => {
|
||||||
|
const res = CivitaiModel.parse(a);
|
||||||
|
console.log(a);
|
||||||
|
console.log(res);
|
||||||
|
setModelRes(res);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
controller.abort();
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
const modifiedField = {
|
||||||
|
...fieldProps,
|
||||||
|
// onChange: (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// handleSearch(event.target.value);
|
||||||
|
// },
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem>
|
||||||
|
{fieldConfigItem.inputProps?.showLabel && (
|
||||||
|
<FormLabel>
|
||||||
|
{label}
|
||||||
|
{isRequired && <span className="text-destructive">*</span>}
|
||||||
|
</FormLabel>
|
||||||
|
)}
|
||||||
|
<FormControl>
|
||||||
|
<AutoFormInput
|
||||||
|
{...props}
|
||||||
|
fieldProps={modifiedField}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
export const customInputNodes: Record<string, string> = {
|
export const customInputNodes: Record<string, string> = {
|
||||||
ComfyUIDeployExternalText: "string",
|
ComfyUIDeployExternalText: "string",
|
||||||
ComfyUIDeployExternalImage: "string - (public image url)",
|
ComfyUIDeployExternalImage: "string - (public image url)",
|
||||||
ComfyUIDeployExternalImageAlpha: "string - (public image url)",
|
ComfyUIDeployExternalImageAlpha: "string - (public image url)",
|
||||||
ComfyUIDeployExternalNumber: "float",
|
ComfyUIDeployExternalNumber: "float",
|
||||||
ComfyUIDeployExternalNumberInt: "integer",
|
ComfyUIDeployExternalNumberInt: "integer",
|
||||||
ComfyUIDeployExternalLora: "string - (public lora download url)",
|
ComfyUIDeployExternalLora: "string - (public lora download url)",
|
||||||
ComfyUIDeployExternalCheckpoints:
|
|
||||||
"string - (public checkpoints download url)",
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const FeedbackThanks = forwardRef<React.ElementRef<'div'>>(
|
|||||||
)
|
)
|
||||||
|
|
||||||
export function Feedback() {
|
export function Feedback() {
|
||||||
const [submitted, setSubmitted] = useState(false)
|
let [submitted, setSubmitted] = useState(false)
|
||||||
|
|
||||||
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export function Prose<T extends React.ElementType = 'div'>({
|
|||||||
as?: T
|
as?: T
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const Component = as ?? 'div'
|
let Component = as ?? 'div'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ function MoonIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ThemeToggle() {
|
export function ThemeToggle() {
|
||||||
const { resolvedTheme, setTheme } = useTheme()
|
let { resolvedTheme, setTheme } = useTheme()
|
||||||
const otherTheme = resolvedTheme === 'dark' ? 'light' : 'dark'
|
let otherTheme = resolvedTheme === 'dark' ? 'light' : 'dark'
|
||||||
const [mounted, setMounted] = useState(false)
|
let [mounted, setMounted] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true)
|
setMounted(true)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import AutoFormSwitch from "./fields/switch";
|
|||||||
import AutoFormTextarea from "./fields/textarea";
|
import AutoFormTextarea from "./fields/textarea";
|
||||||
import AutoFormModelsPicker from "@/components/custom-form/model-picker";
|
import AutoFormModelsPicker from "@/components/custom-form/model-picker";
|
||||||
import AutoFormSnapshotPicker from "@/components/custom-form/snapshot-picker";
|
import AutoFormSnapshotPicker from "@/components/custom-form/snapshot-picker";
|
||||||
|
import AutoFormCheckpointInput from "@/components/custom-form/checkpoint-input";
|
||||||
|
|
||||||
export const INPUT_COMPONENTS = {
|
export const INPUT_COMPONENTS = {
|
||||||
checkbox: AutoFormCheckbox,
|
checkbox: AutoFormCheckbox,
|
||||||
@@ -22,6 +23,7 @@ export const INPUT_COMPONENTS = {
|
|||||||
// Customs
|
// Customs
|
||||||
snapshot: AutoFormSnapshotPicker,
|
snapshot: AutoFormSnapshotPicker,
|
||||||
models: AutoFormModelsPicker,
|
models: AutoFormModelsPicker,
|
||||||
|
checkpoints: AutoFormCheckpointInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-9
@@ -2,18 +2,10 @@ import * as schema from "./schema";
|
|||||||
import { neonConfig, Pool } from "@neondatabase/serverless";
|
import { neonConfig, Pool } from "@neondatabase/serverless";
|
||||||
import { drizzle as neonDrizzle } from "drizzle-orm/neon-serverless";
|
import { drizzle as neonDrizzle } from "drizzle-orm/neon-serverless";
|
||||||
|
|
||||||
const isDevContainer = process.env.REMOTE_CONTAINERS !== undefined;
|
|
||||||
|
|
||||||
// if we're running locally
|
// if we're running locally
|
||||||
if (process.env.VERCEL_ENV !== "production") {
|
if (process.env.VERCEL_ENV !== "production") {
|
||||||
// Set the WebSocket proxy to work with the local instance
|
// Set the WebSocket proxy to work with the local instance
|
||||||
if (isDevContainer) {
|
neonConfig.wsProxy = (host) => `${host}:5481/v1`;
|
||||||
// Running inside a VS Code devcontainer
|
|
||||||
neonConfig.wsProxy = (host) => `host.docker.internal:5481/v1`;
|
|
||||||
} else {
|
|
||||||
// Not running inside a VS Code devcontainer
|
|
||||||
neonConfig.wsProxy = (host) => `${host}:5481/v1`;
|
|
||||||
}
|
|
||||||
// Disable all authentication and encryption
|
// Disable all authentication and encryption
|
||||||
neonConfig.useSecureWebSocket = false;
|
neonConfig.useSecureWebSocket = false;
|
||||||
neonConfig.pipelineTLS = false;
|
neonConfig.pipelineTLS = false;
|
||||||
|
|||||||
+106
-21
@@ -1,3 +1,4 @@
|
|||||||
|
import { CivitaiModelResponse } from "@/types/civitai";
|
||||||
import { type InferSelectModel, relations } from "drizzle-orm";
|
import { type InferSelectModel, relations } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
@@ -8,7 +9,6 @@ import {
|
|||||||
text,
|
text,
|
||||||
timestamp,
|
timestamp,
|
||||||
uuid,
|
uuid,
|
||||||
real,
|
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -88,7 +88,7 @@ export const workflowVersionRelations = relations(
|
|||||||
fields: [workflowVersionTable.workflow_id],
|
fields: [workflowVersionTable.workflow_id],
|
||||||
references: [workflowTable.id],
|
references: [workflowTable.id],
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const workflowRunStatus = pgEnum("workflow_run_status", [
|
export const workflowRunStatus = pgEnum("workflow_run_status", [
|
||||||
@@ -137,10 +137,11 @@ export const workflowRunsTable = dbSchema.table("workflow_runs", {
|
|||||||
() => workflowVersionTable.id,
|
() => workflowVersionTable.id,
|
||||||
{
|
{
|
||||||
onDelete: "set null",
|
onDelete: "set null",
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
workflow_inputs:
|
workflow_inputs: jsonb("workflow_inputs").$type<
|
||||||
jsonb("workflow_inputs").$type<Record<string, string | number>>(),
|
Record<string, string | number>
|
||||||
|
>(),
|
||||||
workflow_id: uuid("workflow_id")
|
workflow_id: uuid("workflow_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => workflowTable.id, {
|
.references(() => workflowTable.id, {
|
||||||
@@ -154,7 +155,6 @@ export const workflowRunsTable = dbSchema.table("workflow_runs", {
|
|||||||
status: workflowRunStatus("status").notNull().default("not-started"),
|
status: workflowRunStatus("status").notNull().default("not-started"),
|
||||||
ended_at: timestamp("ended_at"),
|
ended_at: timestamp("ended_at"),
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
started_at: timestamp("started_at"),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const workflowRunRelations = relations(
|
export const workflowRunRelations = relations(
|
||||||
@@ -173,7 +173,7 @@ export const workflowRunRelations = relations(
|
|||||||
fields: [workflowRunsTable.workflow_id],
|
fields: [workflowRunsTable.workflow_id],
|
||||||
references: [workflowTable.id],
|
references: [workflowTable.id],
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// We still want to keep the workflow run record.
|
// We still want to keep the workflow run record.
|
||||||
@@ -197,7 +197,7 @@ export const workflowOutputRelations = relations(
|
|||||||
fields: [workflowRunOutputs.run_id],
|
fields: [workflowRunOutputs.run_id],
|
||||||
references: [workflowRunsTable.id],
|
references: [workflowRunsTable.id],
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// when user delete, also delete all the workflow versions
|
// when user delete, also delete all the workflow versions
|
||||||
@@ -230,7 +230,7 @@ export const snapshotType = z.object({
|
|||||||
z.object({
|
z.object({
|
||||||
hash: z.string(),
|
hash: z.string(),
|
||||||
disabled: z.boolean(),
|
disabled: z.boolean(),
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
file_custom_nodes: z.array(z.any()),
|
file_custom_nodes: z.array(z.any()),
|
||||||
});
|
});
|
||||||
@@ -245,7 +245,7 @@ export const showcaseMedia = z.array(
|
|||||||
z.object({
|
z.object({
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
isCover: z.boolean().default(false),
|
isCover: z.boolean().default(false),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const showcaseMediaNullable = z
|
export const showcaseMediaNullable = z
|
||||||
@@ -253,7 +253,7 @@ export const showcaseMediaNullable = z
|
|||||||
z.object({
|
z.object({
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
isCover: z.boolean().default(false),
|
isCover: z.boolean().default(false),
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.nullable();
|
.nullable();
|
||||||
|
|
||||||
@@ -276,10 +276,10 @@ export const deploymentsTable = dbSchema.table("deployments", {
|
|||||||
machine_id: uuid("machine_id")
|
machine_id: uuid("machine_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => machinesTable.id),
|
.references(() => machinesTable.id),
|
||||||
share_slug: text("share_slug").unique(),
|
|
||||||
description: text("description"),
|
description: text("description"),
|
||||||
showcase_media:
|
showcase_media: jsonb("showcase_media").$type<
|
||||||
jsonb("showcase_media").$type<z.infer<typeof showcaseMedia>>(),
|
z.infer<typeof showcaseMedia>
|
||||||
|
>(),
|
||||||
environment: deploymentEnvironment("environment").notNull(),
|
environment: deploymentEnvironment("environment").notNull(),
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
@@ -332,22 +332,107 @@ export const apiKeyTable = dbSchema.table("api_keys", {
|
|||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userUsageTable = dbSchema.table("user_usage", {
|
export const resourceUpload = pgEnum("resource_upload", [
|
||||||
|
"started",
|
||||||
|
"error",
|
||||||
|
"succeded",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const modelUploadType = pgEnum("model_upload_type", [
|
||||||
|
"civitai",
|
||||||
|
"huggingface",
|
||||||
|
"other",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const checkpointTable = dbSchema.table("checkpoints", {
|
||||||
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
||||||
|
user_id: text("user_id")
|
||||||
|
.references(() => usersTable.id, {}), // perhaps a "special" user_id for global checkpoints
|
||||||
org_id: text("org_id"),
|
org_id: text("org_id"),
|
||||||
|
description: text("description"),
|
||||||
|
|
||||||
|
checkpoint_volume_id: uuid("checkpoint_volume_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => workflowRunsTable.id, {
|
||||||
|
onDelete: "cascade",
|
||||||
|
}).notNull(),
|
||||||
|
|
||||||
|
model_name: text("model_name"),
|
||||||
|
|
||||||
|
civitai_id: text("civitai_id"),
|
||||||
|
civitai_version_id: text("civitai_version_id"),
|
||||||
|
civitai_url: text("civitai_url"),
|
||||||
|
civitai_download_url: text("civitai_download_url"),
|
||||||
|
civitai_model_response: jsonb("civitai_model_response").$type<
|
||||||
|
z.infer<typeof CivitaiModelResponse>
|
||||||
|
>(),
|
||||||
|
|
||||||
|
hf_url: text("hf_url"),
|
||||||
|
s3_url: text("s3_url"),
|
||||||
|
user_url: text("client_url"),
|
||||||
|
|
||||||
|
is_public: boolean("is_public").notNull().default(false),
|
||||||
|
status: resourceUpload("status").notNull().default("started"),
|
||||||
|
upload_machine_id: text("upload_machine_id"),
|
||||||
|
upload_type: modelUploadType("upload_type").notNull(),
|
||||||
|
build_log: text("build_log"),
|
||||||
|
|
||||||
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const insertCivitaiCheckpointSchema = createInsertSchema(
|
||||||
|
checkpointTable,
|
||||||
|
{
|
||||||
|
civitai_url: (schema) =>
|
||||||
|
schema.civitai_url.trim().url({ message: "URL required" }).includes(
|
||||||
|
"civitai.com/models",
|
||||||
|
{ message: "civitai.com/models link required" },
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const checkpointVolumeTable = dbSchema.table("checkpoint_volume", {
|
||||||
|
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
||||||
user_id: text("user_id")
|
user_id: text("user_id")
|
||||||
.references(() => usersTable.id, {
|
.references(() => usersTable.id, {
|
||||||
onDelete: "cascade",
|
// onDelete: "cascade",
|
||||||
})
|
}),
|
||||||
.notNull(),
|
org_id: text("org_id"),
|
||||||
usage_time: real("usage_time").default(0).notNull(),
|
volume_name: text("volume_name").notNull(),
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
ended_at: timestamp("ended_at").defaultNow().notNull(),
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
|
disabled: boolean("disabled").default(false).notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const checkpointRelations = relations(checkpointTable, ({ one }) => ({
|
||||||
|
user: one(usersTable, {
|
||||||
|
fields: [checkpointTable.user_id],
|
||||||
|
references: [usersTable.id],
|
||||||
|
}),
|
||||||
|
volume: one(checkpointVolumeTable, {
|
||||||
|
fields: [checkpointTable.checkpoint_volume_id],
|
||||||
|
references: [checkpointVolumeTable.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const checkpointVolumeRelations = relations(
|
||||||
|
checkpointVolumeTable,
|
||||||
|
({ many, one }) => ({
|
||||||
|
checkpoint: many(checkpointTable),
|
||||||
|
user: one(usersTable, {
|
||||||
|
fields: [checkpointVolumeTable.user_id],
|
||||||
|
references: [usersTable.id],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export type UserType = InferSelectModel<typeof usersTable>;
|
export type UserType = InferSelectModel<typeof usersTable>;
|
||||||
export type WorkflowType = InferSelectModel<typeof workflowTable>;
|
export type WorkflowType = InferSelectModel<typeof workflowTable>;
|
||||||
export type MachineType = InferSelectModel<typeof machinesTable>;
|
export type MachineType = InferSelectModel<typeof machinesTable>;
|
||||||
export type WorkflowVersionType = InferSelectModel<typeof workflowVersionTable>;
|
export type WorkflowVersionType = InferSelectModel<typeof workflowVersionTable>;
|
||||||
export type DeploymentType = InferSelectModel<typeof deploymentsTable>;
|
export type DeploymentType = InferSelectModel<typeof deploymentsTable>;
|
||||||
export type UserUsageType = InferSelectModel<typeof userUsageTable>;
|
export type CheckpointType = InferSelectModel<typeof checkpointTable>;
|
||||||
|
export type CheckpointVolumeType = InferSelectModel<
|
||||||
|
typeof checkpointVolumeTable
|
||||||
|
>;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
export function remToPx(remValue: number) {
|
export function remToPx(remValue: number) {
|
||||||
const rootFontSize =
|
let rootFontSize =
|
||||||
typeof window === "undefined"
|
typeof window === 'undefined'
|
||||||
? 16
|
? 16
|
||||||
: parseFloat(
|
: parseFloat(window.getComputedStyle(document.documentElement).fontSize)
|
||||||
window.getComputedStyle(document.documentElement).fontSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
return remValue * rootFontSize
|
return remValue * rootFontSize
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -27,13 +27,13 @@ function rehypeShiki() {
|
|||||||
|
|
||||||
visit(tree, "element", (node) => {
|
visit(tree, "element", (node) => {
|
||||||
if (node.tagName === "pre" && node.children[0]?.tagName === "code") {
|
if (node.tagName === "pre" && node.children[0]?.tagName === "code") {
|
||||||
const codeNode = node.children[0];
|
let codeNode = node.children[0];
|
||||||
const textNode = codeNode.children[0];
|
let textNode = codeNode.children[0];
|
||||||
|
|
||||||
node.properties.code = textNode.value;
|
node.properties.code = textNode.value;
|
||||||
|
|
||||||
if (node.properties.language) {
|
if (node.properties.language) {
|
||||||
const tokens = highlighter.codeToThemedTokens(
|
let tokens = highlighter.codeToThemedTokens(
|
||||||
textNode.value,
|
textNode.value,
|
||||||
node.properties.language
|
node.properties.language
|
||||||
);
|
);
|
||||||
@@ -53,7 +53,7 @@ function rehypeShiki() {
|
|||||||
|
|
||||||
function rehypeSlugify() {
|
function rehypeSlugify() {
|
||||||
return (tree) => {
|
return (tree) => {
|
||||||
const slugify = slugifyWithCounter();
|
let slugify = slugifyWithCounter();
|
||||||
visit(tree, "element", (node) => {
|
visit(tree, "element", (node) => {
|
||||||
if (node.tagName === "h2" && !node.properties.id) {
|
if (node.tagName === "h2" && !node.properties.id) {
|
||||||
node.properties.id = slugify(toString(node));
|
node.properties.id = slugify(toString(node));
|
||||||
@@ -64,10 +64,10 @@ function rehypeSlugify() {
|
|||||||
|
|
||||||
function rehypeAddMDXExports(getExports) {
|
function rehypeAddMDXExports(getExports) {
|
||||||
return (tree) => {
|
return (tree) => {
|
||||||
const exports = Object.entries(getExports(tree));
|
let exports = Object.entries(getExports(tree));
|
||||||
|
|
||||||
for (const [name, value] of exports) {
|
for (let [name, value] of exports) {
|
||||||
for (const node of tree.children) {
|
for (let node of tree.children) {
|
||||||
if (
|
if (
|
||||||
node.type === "mdxjsEsm" &&
|
node.type === "mdxjsEsm" &&
|
||||||
new RegExp(`export\\s+const\\s+${name}\\s*=`).test(node.value)
|
new RegExp(`export\\s+const\\s+${name}\\s*=`).test(node.value)
|
||||||
@@ -76,7 +76,7 @@ function rehypeAddMDXExports(getExports) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportStr = `export const ${name} = ${value}`;
|
let exportStr = `export const ${name} = ${value}`;
|
||||||
|
|
||||||
tree.children.push({
|
tree.children.push({
|
||||||
type: "mdxjsEsm",
|
type: "mdxjsEsm",
|
||||||
@@ -93,9 +93,9 @@ function rehypeAddMDXExports(getExports) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getSections(node) {
|
function getSections(node) {
|
||||||
const sections = [];
|
let sections = [];
|
||||||
|
|
||||||
for (const child of node.children ?? []) {
|
for (let child of node.children ?? []) {
|
||||||
if (child.type === "element" && child.tagName === "h2") {
|
if (child.type === "element" && child.tagName === "h2") {
|
||||||
sections.push(`{
|
sections.push(`{
|
||||||
title: ${JSON.stringify(toString(child))},
|
title: ${JSON.stringify(toString(child))},
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ function extractSections() {
|
|||||||
|
|
||||||
visit(tree, (node) => {
|
visit(tree, (node) => {
|
||||||
if (node.type === "heading" || node.type === "paragraph") {
|
if (node.type === "heading" || node.type === "paragraph") {
|
||||||
const content = toString(excludeObjectExpressions(node));
|
let content = toString(excludeObjectExpressions(node));
|
||||||
if (node.type === "heading" && node.depth <= 2) {
|
if (node.type === "heading" && node.depth <= 2) {
|
||||||
const hash = node.depth === 1 ? null : slugify(content);
|
let hash = node.depth === 1 ? null : slugify(content);
|
||||||
sections.push([content, hash, []]);
|
sections.push([content, hash, []]);
|
||||||
} else {
|
} else {
|
||||||
sections.at(-1)?.[2].push(content);
|
sections.at(-1)?.[2].push(content);
|
||||||
@@ -45,7 +45,7 @@ function extractSections() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function (nextConfig = {}) {
|
export default function (nextConfig = {}) {
|
||||||
const cache = new Map();
|
let cache = new Map();
|
||||||
|
|
||||||
return Object.assign({}, nextConfig, {
|
return Object.assign({}, nextConfig, {
|
||||||
webpack(config, options) {
|
webpack(config, options) {
|
||||||
@@ -53,20 +53,20 @@ export default function (nextConfig = {}) {
|
|||||||
test: __filename,
|
test: __filename,
|
||||||
use: [
|
use: [
|
||||||
createLoader(function () {
|
createLoader(function () {
|
||||||
const appDir = path.resolve("./src/app/(docs)/docs");
|
let appDir = path.resolve("./src/app/(docs)/docs");
|
||||||
this.addContextDependency(appDir);
|
this.addContextDependency(appDir);
|
||||||
|
|
||||||
const files = glob.sync("**/*.mdx", { cwd: appDir });
|
let files = glob.sync("**/*.mdx", { cwd: appDir });
|
||||||
const data = files.map((file) => {
|
let data = files.map((file) => {
|
||||||
let url = `/${file.replace(/(^|\/)page\.mdx$/, "")}`;
|
let url = `/${file.replace(/(^|\/)page\.mdx$/, "")}`;
|
||||||
const mdx = fs.readFileSync(path.join(appDir, file), "utf8");
|
let mdx = fs.readFileSync(path.join(appDir, file), "utf8");
|
||||||
|
|
||||||
let sections = [];
|
let sections = [];
|
||||||
|
|
||||||
if (cache.get(file)?.[0] === mdx) {
|
if (cache.get(file)?.[0] === mdx) {
|
||||||
sections = cache.get(file)[1];
|
sections = cache.get(file)[1];
|
||||||
} else {
|
} else {
|
||||||
const vfile = { value: mdx, sections };
|
let vfile = { value: mdx, sections };
|
||||||
processor.runSync(processor.parse(vfile), vfile);
|
processor.runSync(processor.parse(vfile), vfile);
|
||||||
cache.set(file, [mdx, sections]);
|
cache.set(file, [mdx, sections]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import type { ResponseConfig } from "@asteasolutions/zod-to-openapi";
|
import type { ResponseConfig } from "@asteasolutions/zod-to-openapi";
|
||||||
|
|
||||||
|
|
||||||
import { z } from "@hono/zod-openapi";
|
import { z } from "@hono/zod-openapi";
|
||||||
|
|
||||||
export const authError = {
|
export const authError = {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { insertCivitaiCheckpointSchema } from "@/db/schema";
|
||||||
|
|
||||||
|
export const addCivitaiCheckpointSchema = insertCivitaiCheckpointSchema.pick({
|
||||||
|
civitai_url: true,
|
||||||
|
});
|
||||||
+197
-206
@@ -1,10 +1,11 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
|
import { withServerPromise } from "./withServerPromise";
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import type {
|
import type {
|
||||||
MachineType,
|
MachineType,
|
||||||
WorkflowRunOriginType,
|
WorkflowRunOriginType,
|
||||||
WorkflowVersionType,
|
WorkflowVersionType,
|
||||||
} from "@/db/schema";
|
} from "@/db/schema";
|
||||||
import { machinesTable, workflowRunsTable } from "@/db/schema";
|
import { machinesTable, workflowRunsTable } from "@/db/schema";
|
||||||
import type { APIKeyUserType } from "@/server/APIKeyBodyRequest";
|
import type { APIKeyUserType } from "@/server/APIKeyBodyRequest";
|
||||||
@@ -15,229 +16,219 @@ import { and, eq } from "drizzle-orm";
|
|||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
import "server-only";
|
import "server-only";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { withServerPromise } from "./withServerPromise";
|
|
||||||
|
|
||||||
export const createRun = withServerPromise(
|
export const createRun = withServerPromise(
|
||||||
async ({
|
async ({
|
||||||
origin,
|
origin,
|
||||||
workflow_version_id,
|
workflow_version_id,
|
||||||
machine_id,
|
machine_id,
|
||||||
inputs,
|
inputs,
|
||||||
runOrigin,
|
runOrigin,
|
||||||
apiUser,
|
apiUser,
|
||||||
}: {
|
}: {
|
||||||
origin: string;
|
origin: string;
|
||||||
workflow_version_id: string | WorkflowVersionType;
|
workflow_version_id: string | WorkflowVersionType;
|
||||||
machine_id: string | MachineType;
|
machine_id: string | MachineType;
|
||||||
inputs?: Record<string, string | number>;
|
inputs?: Record<string, string | number>;
|
||||||
runOrigin?: WorkflowRunOriginType;
|
runOrigin?: WorkflowRunOriginType;
|
||||||
apiUser?: APIKeyUserType;
|
apiUser?: APIKeyUserType;
|
||||||
}) => {
|
}) => {
|
||||||
const machine =
|
const machine =
|
||||||
typeof machine_id === "string"
|
typeof machine_id === "string"
|
||||||
? await db.query.machinesTable.findFirst({
|
? await db.query.machinesTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(machinesTable.id, machine_id),
|
eq(machinesTable.id, machine_id),
|
||||||
eq(machinesTable.disabled, false),
|
eq(machinesTable.disabled, false)
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
: machine_id;
|
: machine_id;
|
||||||
|
|
||||||
if (!machine) {
|
if (!machine) {
|
||||||
throw new Error("Machine not found");
|
throw new Error("Machine not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflow_version_data =
|
const workflow_version_data =
|
||||||
typeof workflow_version_id === "string"
|
typeof workflow_version_id === "string"
|
||||||
? await db.query.workflowVersionTable.findFirst({
|
? await db.query.workflowVersionTable.findFirst({
|
||||||
where: eq(workflowRunsTable.id, workflow_version_id),
|
where: eq(workflowRunsTable.id, workflow_version_id),
|
||||||
with: {
|
with: {
|
||||||
workflow: {
|
workflow: {
|
||||||
columns: {
|
columns: {
|
||||||
org_id: true,
|
org_id: true,
|
||||||
user_id: true,
|
user_id: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
: workflow_version_id;
|
: workflow_version_id;
|
||||||
|
|
||||||
if (!workflow_version_data) {
|
if (!workflow_version_data) {
|
||||||
throw new Error("Workflow version not found");
|
throw new Error("Workflow version not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (apiUser)
|
if (apiUser)
|
||||||
if (apiUser.org_id) {
|
if (apiUser.org_id) {
|
||||||
// is org api call, check org only
|
// is org api call, check org only
|
||||||
if (apiUser.org_id != workflow_version_data.workflow.org_id) {
|
if (apiUser.org_id != workflow_version_data.workflow.org_id) {
|
||||||
throw new Error("Workflow not found");
|
throw new Error("Workflow not found");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// is user api call, check user only
|
// is user api call, check user only
|
||||||
if (
|
if (
|
||||||
apiUser.user_id != workflow_version_data.workflow.user_id &&
|
apiUser.user_id != workflow_version_data.workflow.user_id &&
|
||||||
workflow_version_data.workflow.org_id == null
|
workflow_version_data.workflow.org_id == null
|
||||||
) {
|
) {
|
||||||
throw new Error("Workflow not found");
|
throw new Error("Workflow not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflow_api = workflow_version_data.workflow_api;
|
const workflow_api = workflow_version_data.workflow_api;
|
||||||
|
|
||||||
// Replace the inputs
|
// Replace the inputs
|
||||||
if (inputs && workflow_api) {
|
if (inputs && workflow_api) {
|
||||||
for (const key in inputs) {
|
for (const key in inputs) {
|
||||||
Object.entries(workflow_api).forEach(([_, node]) => {
|
Object.entries(workflow_api).forEach(([_, node]) => {
|
||||||
if (node.inputs["input_id"] === key) {
|
if (node.inputs["input_id"] === key) {
|
||||||
node.inputs["input_id"] = inputs[key];
|
node.inputs["input_id"] = inputs[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let prompt_id: string | undefined = undefined;
|
let prompt_id: string | undefined = undefined;
|
||||||
const shareData = {
|
const shareData = {
|
||||||
workflow_api: workflow_api,
|
workflow_api: workflow_api,
|
||||||
status_endpoint: `${origin}/api/update-run`,
|
status_endpoint: `${origin}/api/update-run`,
|
||||||
file_upload_endpoint: `${origin}/api/file-upload`,
|
file_upload_endpoint: `${origin}/api/file-upload`,
|
||||||
};
|
};
|
||||||
|
|
||||||
prompt_id = v4();
|
prompt_id = v4();
|
||||||
|
|
||||||
// Add to our db
|
// Add to our db
|
||||||
const workflow_run = await db
|
const workflow_run = await db
|
||||||
.insert(workflowRunsTable)
|
.insert(workflowRunsTable)
|
||||||
.values({
|
.values({
|
||||||
id: prompt_id,
|
id: prompt_id,
|
||||||
workflow_id: workflow_version_data.workflow_id,
|
workflow_id: workflow_version_data.workflow_id,
|
||||||
workflow_version_id: workflow_version_data.id,
|
workflow_version_id: workflow_version_data.id,
|
||||||
workflow_inputs: inputs,
|
workflow_inputs: inputs,
|
||||||
machine_id: machine.id,
|
machine_id: machine.id,
|
||||||
origin: runOrigin,
|
origin: runOrigin,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
revalidatePath(`/${workflow_version_data.workflow_id}`);
|
revalidatePath(`/${workflow_version_data.workflow_id}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (machine.type) {
|
switch (machine.type) {
|
||||||
case "comfy-deploy-serverless":
|
case "comfy-deploy-serverless":
|
||||||
case "modal-serverless":
|
case "modal-serverless":
|
||||||
const _data = {
|
const _data = {
|
||||||
input: {
|
input: {
|
||||||
...shareData,
|
...shareData,
|
||||||
prompt_id: prompt_id,
|
prompt_id: prompt_id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ___result = await fetch(`${machine.endpoint}/run`, {
|
const ___result = await fetch(`${machine.endpoint}/run`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(_data),
|
body: JSON.stringify(_data),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
});
|
});
|
||||||
console.log(___result);
|
console.log(___result);
|
||||||
if (!___result.ok)
|
if (!___result.ok)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error creating run, ${
|
`Error creating run, ${
|
||||||
___result.statusText
|
___result.statusText
|
||||||
} ${await ___result.text()}`,
|
} ${await ___result.text()}`
|
||||||
);
|
);
|
||||||
console.log(_data, ___result);
|
console.log(_data, ___result);
|
||||||
break;
|
break;
|
||||||
case "runpod-serverless":
|
case "runpod-serverless":
|
||||||
const data = {
|
const data = {
|
||||||
input: {
|
input: {
|
||||||
...shareData,
|
...shareData,
|
||||||
prompt_id: prompt_id,
|
prompt_id: prompt_id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!machine.auth_token &&
|
!machine.auth_token &&
|
||||||
!machine.endpoint.includes("localhost") &&
|
!machine.endpoint.includes("localhost") &&
|
||||||
!machine.endpoint.includes("127.0.0.1")
|
!machine.endpoint.includes("127.0.0.1")
|
||||||
) {
|
) {
|
||||||
throw new Error("Machine auth token not found");
|
throw new Error("Machine auth token not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const __result = await fetch(`${machine.endpoint}/run`, {
|
const __result = await fetch(`${machine.endpoint}/run`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${machine.auth_token}`,
|
Authorization: `Bearer ${machine.auth_token}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
});
|
});
|
||||||
console.log(__result);
|
console.log(__result);
|
||||||
if (!__result.ok)
|
if (!__result.ok)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error creating run, ${
|
`Error creating run, ${
|
||||||
__result.statusText
|
__result.statusText
|
||||||
} ${await __result.text()}`,
|
} ${await __result.text()}`
|
||||||
);
|
);
|
||||||
console.log(data, __result);
|
console.log(data, __result);
|
||||||
break;
|
break;
|
||||||
case "classic":
|
case "classic":
|
||||||
const body = {
|
const body = {
|
||||||
...shareData,
|
...shareData,
|
||||||
prompt_id: prompt_id,
|
prompt_id: prompt_id,
|
||||||
};
|
};
|
||||||
// console.log(body);
|
// console.log(body);
|
||||||
const comfyui_endpoint = `${machine.endpoint}/comfyui-deploy/run`;
|
const comfyui_endpoint = `${machine.endpoint}/comfyui-deploy/run`;
|
||||||
const _result = await fetch(comfyui_endpoint, {
|
const _result = await fetch(comfyui_endpoint, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
});
|
});
|
||||||
// console.log(_result);
|
// console.log(_result);
|
||||||
|
|
||||||
if (!_result.ok) {
|
if (!_result.ok) {
|
||||||
let message = `Error creating run, ${_result.statusText}`;
|
let message = `Error creating run, ${_result.statusText}`;
|
||||||
try {
|
try {
|
||||||
const result = await ComfyAPI_Run.parseAsync(
|
const result = await ComfyAPI_Run.parseAsync(
|
||||||
await _result.json(),
|
await _result.json()
|
||||||
);
|
);
|
||||||
message += ` ${result.node_errors}`;
|
message += ` ${result.node_errors}`;
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
// prompt_id = result.prompt_id;
|
// prompt_id = result.prompt_id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
await db
|
await db
|
||||||
.update(workflowRunsTable)
|
.update(workflowRunsTable)
|
||||||
.set({
|
.set({
|
||||||
status: "failed",
|
status: "failed",
|
||||||
})
|
})
|
||||||
.where(eq(workflowRunsTable.id, workflow_run[0].id));
|
.where(eq(workflowRunsTable.id, workflow_run[0].id));
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It successfully started, update the started_at time
|
return {
|
||||||
|
workflow_run_id: workflow_run[0].id,
|
||||||
await db
|
message: "Successful workflow run",
|
||||||
.update(workflowRunsTable)
|
};
|
||||||
.set({
|
}
|
||||||
started_at: new Date(),
|
|
||||||
})
|
|
||||||
.where(eq(workflowRunsTable.id, workflow_run[0].id));
|
|
||||||
|
|
||||||
return {
|
|
||||||
workflow_run_id: workflow_run[0].id,
|
|
||||||
message: "Successful workflow run",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export async function checkStatus(run_id: string) {
|
export async function checkStatus(run_id: string) {
|
||||||
const { userId } = auth();
|
const { userId } = auth();
|
||||||
if (!userId) throw new Error("User not found");
|
if (!userId) throw new Error("User not found");
|
||||||
|
|
||||||
return await getRunsData(run_id);
|
return await getRunsData(run_id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { auth } from "@clerk/nextjs";
|
||||||
|
import {
|
||||||
|
checkpointTable,
|
||||||
|
CheckpointType,
|
||||||
|
volumeTable,
|
||||||
|
CheckpointVolumeType,
|
||||||
|
} from "@/db/schema";
|
||||||
|
import { withServerPromise } from "./withServerPromise";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { db } from "@/db/db";
|
||||||
|
import type { z } from "zod";
|
||||||
|
import { headers } from "next/headers";
|
||||||
|
import { addCivitaiCheckpointSchema } from "./addCheckpointSchema";
|
||||||
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
|
import { CivitaiModelResponse } from "@/types/civitai";
|
||||||
|
|
||||||
|
export async function getCheckpoints() {
|
||||||
|
const { userId, orgId } = auth();
|
||||||
|
if (!userId) throw new Error("No user id");
|
||||||
|
const checkpoints = await db
|
||||||
|
.select()
|
||||||
|
.from(checkpointTable)
|
||||||
|
.where(
|
||||||
|
orgId
|
||||||
|
? eq(checkpointTable.org_id, orgId)
|
||||||
|
// make sure org_id is null
|
||||||
|
: and(
|
||||||
|
eq(checkpointTable.user_id, userId),
|
||||||
|
isNull(checkpointTable.org_id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return checkpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCheckpointById(id: string) {
|
||||||
|
const { userId, orgId } = auth();
|
||||||
|
if (!userId) throw new Error("No user id");
|
||||||
|
const checkpoint = await db
|
||||||
|
.select()
|
||||||
|
.from(checkpointTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
orgId ? eq(checkpointTable.org_id, orgId) : and(
|
||||||
|
eq(checkpointTable.user_id, userId),
|
||||||
|
isNull(checkpointTable.org_id),
|
||||||
|
),
|
||||||
|
eq(checkpointTable.id, id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return checkpoint[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCheckpointVolumes() {
|
||||||
|
const { userId, orgId } = auth();
|
||||||
|
if (!userId) throw new Error("No user id");
|
||||||
|
const checkpointVolume = await db
|
||||||
|
.select()
|
||||||
|
.from(volumeTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
orgId
|
||||||
|
? eq(volumeTable.org_id, orgId)
|
||||||
|
// make sure org_id is null
|
||||||
|
: and(
|
||||||
|
eq(volumeTable.user_id, userId),
|
||||||
|
isNull(volumeTable.org_id),
|
||||||
|
),
|
||||||
|
eq(volumeTable.disabled, false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return checkpointVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addCheckpointVolume() {
|
||||||
|
const { userId, orgId } = auth();
|
||||||
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
|
// Insert the new volume into the checkpointVolumeTable
|
||||||
|
const insertedVolume = await db
|
||||||
|
.insert(volumeTable)
|
||||||
|
.values({
|
||||||
|
user_id: userId,
|
||||||
|
org_id: orgId,
|
||||||
|
volume_name: `checkpoints_${userId}`,
|
||||||
|
// created_at and updated_at will be set to current timestamp by default
|
||||||
|
disabled: false, // Default value
|
||||||
|
})
|
||||||
|
.returning(); // Returns the inserted row
|
||||||
|
|
||||||
|
return insertedVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUrl(civitai_url: string) {
|
||||||
|
// expect to be a URL to be https://civitai.com/models/36520
|
||||||
|
// possiblity with slugged name and query-param modelVersionId
|
||||||
|
const baseUrl = "https://civitai.com/api/v1/models/";
|
||||||
|
const url = new URL(civitai_url);
|
||||||
|
const pathSegments = url.pathname.split("/");
|
||||||
|
const modelId = pathSegments[pathSegments.indexOf("models") + 1];
|
||||||
|
const modelVersionId = url.searchParams.get("modelVersionId");
|
||||||
|
|
||||||
|
return { url: baseUrl + modelId, modelVersionId };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addCivitaiCheckpoint = withServerPromise(
|
||||||
|
async (data: z.infer<typeof addCivitaiCheckpointSchema>) => {
|
||||||
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
|
if (!data.civitai_url) return { error: "no civitai_url" };
|
||||||
|
if (!userId) return { error: "No user id" };
|
||||||
|
|
||||||
|
const { url, modelVersionId } = getUrl(data?.civitai_url);
|
||||||
|
const civitaiModelRes = await fetch(url)
|
||||||
|
.then((x) => x.json())
|
||||||
|
.then((a) => {
|
||||||
|
return CivitaiModelResponse.parse(a);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (civitaiModelRes?.modelVersions?.length === 0) {
|
||||||
|
return; // no versions to download
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedModelVersion;
|
||||||
|
let selectedModelVersionId: string | null = modelVersionId;
|
||||||
|
if (!selectedModelVersionId) {
|
||||||
|
selectedModelVersion = civitaiModelRes.modelVersions[0];
|
||||||
|
selectedModelVersionId = civitaiModelRes.modelVersions[0].id.toString();
|
||||||
|
} else {
|
||||||
|
selectedModelVersion = civitaiModelRes.modelVersions.find((version) =>
|
||||||
|
version.id.toString() === selectedModelVersionId
|
||||||
|
);
|
||||||
|
if (!selectedModelVersion) {
|
||||||
|
return; // version id is wrong
|
||||||
|
}
|
||||||
|
selectedModelVersionId = selectedModelVersion?.id.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkpointVolumes = await getCheckpointVolumes();
|
||||||
|
let cVolume;
|
||||||
|
if (checkpointVolumes.length === 0) {
|
||||||
|
const volume = await addCheckpointVolume();
|
||||||
|
cVolume = volume[0];
|
||||||
|
} else {
|
||||||
|
cVolume = checkpointVolumes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = await db
|
||||||
|
.insert(checkpointTable)
|
||||||
|
.values({
|
||||||
|
user_id: userId,
|
||||||
|
org_id: orgId,
|
||||||
|
upload_type: "civitai",
|
||||||
|
civitai_id: civitaiModelRes.id.toString(),
|
||||||
|
civitai_version_id: selectedModelVersionId,
|
||||||
|
civitai_url: data.civitai_url,
|
||||||
|
civitai_download_url: selectedModelVersion.downloadUrl,
|
||||||
|
civitai_model_response: civitaiModelRes,
|
||||||
|
checkpoint_volume_id: cVolume.id,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
const b = a[0];
|
||||||
|
|
||||||
|
await uploadCheckpoint(data, b, cVolume);
|
||||||
|
redirect(`/checkpoints/${b.id}`);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
async function uploadCheckpoint(
|
||||||
|
data: z.infer<typeof addCivitaiCheckpointSchema>,
|
||||||
|
b: CheckpointType,
|
||||||
|
v: CheckpointVolumeType,
|
||||||
|
) {
|
||||||
|
const headersList = headers();
|
||||||
|
|
||||||
|
const domain = headersList.get("x-forwarded-host") || "";
|
||||||
|
const protocol = headersList.get("x-forwarded-proto") || "";
|
||||||
|
|
||||||
|
if (domain === "") {
|
||||||
|
throw new Error("No domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call remote builder
|
||||||
|
const result = await fetch(
|
||||||
|
`${process.env.MODAL_BUILDER_URL!}/upload_volume`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
download_url: data.civitai_url,
|
||||||
|
volume_name: v.volume_name,
|
||||||
|
volume_id: v.id,
|
||||||
|
callback_url: `${protocol}://${domain}/api/volume-updated`,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
const error_log = await result.text();
|
||||||
|
await db
|
||||||
|
.update(checkpointTable)
|
||||||
|
.set({
|
||||||
|
...data,
|
||||||
|
status: "error",
|
||||||
|
build_log: error_log,
|
||||||
|
})
|
||||||
|
.where(eq(checkpointTable.id, b.id));
|
||||||
|
throw new Error(`Error: ${result.statusText} ${error_log}`);
|
||||||
|
} else {
|
||||||
|
// setting the build machine id
|
||||||
|
const json = await result.json();
|
||||||
|
await db
|
||||||
|
.update(checkpointTable)
|
||||||
|
.set({
|
||||||
|
...data,
|
||||||
|
upload_machine_id: json.build_machine_instance_id,
|
||||||
|
})
|
||||||
|
.where(eq(checkpointTable.id, b.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
+192
-224
@@ -7,279 +7,247 @@ import { createNewWorkflow } from "@/server/createNewWorkflow";
|
|||||||
import { addCustomMachine } from "@/server/curdMachine";
|
import { addCustomMachine } from "@/server/curdMachine";
|
||||||
import { withServerPromise } from "@/server/withServerPromise";
|
import { withServerPromise } from "@/server/withServerPromise";
|
||||||
import { auth } from "@clerk/nextjs";
|
import { auth } from "@clerk/nextjs";
|
||||||
import { clerkClient } from "@clerk/nextjs/server";
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
import slugify from "@sindresorhus/slugify";
|
|
||||||
import { and, eq, isNull, or } from "drizzle-orm";
|
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import "server-only";
|
import "server-only";
|
||||||
import { validate as isValidUUID } from "uuid";
|
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
export async function createDeployments(
|
export async function createDeployments(
|
||||||
workflow_id: string,
|
workflow_id: string,
|
||||||
version_id: string,
|
version_id: string,
|
||||||
machine_id: string,
|
machine_id: string,
|
||||||
environment: DeploymentType["environment"],
|
environment: DeploymentType["environment"]
|
||||||
) {
|
) {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
if (!machine_id) {
|
if (!machine_id) {
|
||||||
throw new Error("No machine id provided");
|
throw new Error("No machine id provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same environment and same workflow
|
// Same environment and same workflow
|
||||||
const existingDeployment = await db.query.deploymentsTable.findFirst({
|
const existingDeployment = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.workflow_id, workflow_id),
|
eq(deploymentsTable.workflow_id, workflow_id),
|
||||||
eq(deploymentsTable.environment, environment),
|
eq(deploymentsTable.environment, environment)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingDeployment) {
|
if (existingDeployment) {
|
||||||
await db
|
await db
|
||||||
.update(deploymentsTable)
|
.update(deploymentsTable)
|
||||||
.set({
|
.set({
|
||||||
workflow_id,
|
workflow_id,
|
||||||
workflow_version_id: version_id,
|
workflow_version_id: version_id,
|
||||||
machine_id,
|
machine_id,
|
||||||
org_id: orgId,
|
org_id: orgId,
|
||||||
})
|
})
|
||||||
.where(eq(deploymentsTable.id, existingDeployment.id));
|
.where(eq(deploymentsTable.id, existingDeployment.id));
|
||||||
} else {
|
} else {
|
||||||
const workflow = await db.query.workflowTable.findFirst({
|
await db.insert(deploymentsTable).values({
|
||||||
where: eq(workflowTable.id, workflow_id),
|
user_id: userId,
|
||||||
with: {
|
workflow_id,
|
||||||
user: {
|
workflow_version_id: version_id,
|
||||||
columns: {
|
machine_id,
|
||||||
name: true,
|
environment,
|
||||||
},
|
org_id: orgId,
|
||||||
},
|
});
|
||||||
},
|
}
|
||||||
});
|
revalidatePath(`/${workflow_id}`);
|
||||||
|
return {
|
||||||
if (!workflow) throw new Error("No workflow found");
|
message: `Successfully created deployment for ${environment}`,
|
||||||
|
};
|
||||||
const userName = workflow.org_id
|
|
||||||
? await clerkClient.organizations
|
|
||||||
.getOrganization({
|
|
||||||
organizationId: workflow.org_id,
|
|
||||||
})
|
|
||||||
.then((x) => x.name)
|
|
||||||
: workflow.user.name;
|
|
||||||
|
|
||||||
await db.insert(deploymentsTable).values({
|
|
||||||
user_id: userId,
|
|
||||||
workflow_id,
|
|
||||||
workflow_version_id: version_id,
|
|
||||||
machine_id,
|
|
||||||
environment,
|
|
||||||
org_id: orgId,
|
|
||||||
share_slug: slugify(`${userName} ${workflow.name}`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
revalidatePath(`/${workflow_id}`);
|
|
||||||
return {
|
|
||||||
message: `Successfully created deployment for ${environment}`,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findAllDeployments() {
|
export async function findAllDeployments() {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
const deployments = await db.query.workflowTable.findMany({
|
const deployments = await db.query.workflowTable.findMany({
|
||||||
where: and(
|
where: and(
|
||||||
orgId
|
orgId
|
||||||
? eq(workflowTable.org_id, orgId)
|
? eq(workflowTable.org_id, orgId)
|
||||||
: and(eq(workflowTable.user_id, userId), isNull(workflowTable.org_id)),
|
: and(eq(workflowTable.user_id, userId), isNull(workflowTable.org_id))
|
||||||
),
|
),
|
||||||
columns: {
|
columns: {
|
||||||
name: true,
|
name: true,
|
||||||
},
|
},
|
||||||
with: {
|
with: {
|
||||||
deployments: {
|
deployments: {
|
||||||
columns: {
|
columns: {
|
||||||
environment: true,
|
environment: true,
|
||||||
},
|
},
|
||||||
with: {
|
with: {
|
||||||
version: {
|
version: {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
snapshot: true,
|
snapshot: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return deployments;
|
return deployments;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findSharedDeployment(workflow_id: string) {
|
export async function findSharedDeployment(workflow_id: string) {
|
||||||
const deploymentData = await db.query.deploymentsTable.findFirst({
|
const deploymentData = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
isValidUUID(workflow_id)
|
eq(deploymentsTable.id, workflow_id)
|
||||||
? eq(deploymentsTable.id, workflow_id)
|
),
|
||||||
: eq(deploymentsTable.share_slug, workflow_id),
|
with: {
|
||||||
),
|
user: true,
|
||||||
with: {
|
machine: true,
|
||||||
user: true,
|
workflow: {
|
||||||
machine: true,
|
columns: {
|
||||||
workflow: {
|
name: true,
|
||||||
columns: {
|
org_id: true,
|
||||||
name: true,
|
user_id: true,
|
||||||
org_id: true,
|
},
|
||||||
user_id: true,
|
},
|
||||||
},
|
version: true,
|
||||||
},
|
},
|
||||||
version: true,
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return deploymentData;
|
return deploymentData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const removePublicShareDeployment = withServerPromise(
|
export const removePublicShareDeployment = withServerPromise(
|
||||||
async (deployment_id: string) => {
|
async (deployment_id: string) => {
|
||||||
const [removed] = await db
|
await db
|
||||||
.delete(deploymentsTable)
|
.delete(deploymentsTable)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, deployment_id),
|
eq(deploymentsTable.id, deployment_id)
|
||||||
),
|
)
|
||||||
).returning();
|
);
|
||||||
|
}
|
||||||
// revalidatePath(
|
|
||||||
// `/workflows/${removed.workflow_id}`
|
|
||||||
// )
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export const cloneWorkflow = withServerPromise(
|
export const cloneWorkflow = withServerPromise(
|
||||||
async (deployment_id: string) => {
|
async (deployment_id: string) => {
|
||||||
const deployment = await db.query.deploymentsTable.findFirst({
|
const deployment = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, deployment_id),
|
eq(deploymentsTable.id, deployment_id)
|
||||||
),
|
),
|
||||||
with: {
|
with: {
|
||||||
version: true,
|
version: true,
|
||||||
workflow: true,
|
workflow: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!deployment) throw new Error("No deployment found");
|
if (!deployment) throw new Error("No deployment found");
|
||||||
|
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
await createNewWorkflow({
|
await createNewWorkflow({
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
org_id: orgId,
|
org_id: orgId,
|
||||||
workflow_name: `${deployment.workflow.name} (Cloned)`,
|
workflow_name: `${deployment.workflow.name} (Cloned)`,
|
||||||
workflowData: {
|
workflowData: {
|
||||||
workflow: deployment.version.workflow,
|
workflow: deployment.version.workflow,
|
||||||
workflow_api: deployment?.version.workflow_api,
|
workflow_api: deployment?.version.workflow_api,
|
||||||
snapshot: deployment?.version.snapshot,
|
snapshot: deployment?.version.snapshot,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
redirect(`/workflows/${deployment.workflow.id}`);
|
redirect(`/workflows/${deployment.workflow.id}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: "Successfully cloned workflow",
|
message: "Successfully cloned workflow",
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const cloneMachine = withServerPromise(async (deployment_id: string) => {
|
export const cloneMachine = withServerPromise(async (deployment_id: string) => {
|
||||||
const deployment = await db.query.deploymentsTable.findFirst({
|
const deployment = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, deployment_id),
|
eq(deploymentsTable.id, deployment_id)
|
||||||
),
|
),
|
||||||
with: {
|
with: {
|
||||||
machine: true,
|
machine: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!deployment) throw new Error("No deployment found");
|
if (!deployment) throw new Error("No deployment found");
|
||||||
if (deployment.machine.type !== "comfy-deploy-serverless")
|
if (deployment.machine.type !== "comfy-deploy-serverless")
|
||||||
throw new Error("Can only clone comfy-deploy-serverlesss");
|
throw new Error("Can only clone comfy-deploy-serverlesss");
|
||||||
|
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
await addCustomMachine({
|
await addCustomMachine({
|
||||||
gpu: deployment.machine.gpu,
|
gpu: deployment.machine.gpu,
|
||||||
models: deployment.machine.models,
|
models: deployment.machine.models,
|
||||||
snapshot: deployment.machine.snapshot,
|
snapshot: deployment.machine.snapshot,
|
||||||
name: `${deployment.machine.name} (Cloned)`,
|
name: `${deployment.machine.name} (Cloned)`,
|
||||||
type: "comfy-deploy-serverless",
|
type: "comfy-deploy-serverless",
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: "Successfully cloned workflow",
|
message: "Successfully cloned workflow",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function findUserShareDeployment(share_id: string) {
|
export async function findUserShareDeployment(share_id: string) {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
const [deployment] = await db
|
const [deployment] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(deploymentsTable)
|
.from(deploymentsTable)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
isValidUUID(share_id)
|
eq(deploymentsTable.id, share_id),
|
||||||
? eq(deploymentsTable.id, share_id)
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
: eq(deploymentsTable.share_slug, share_id),
|
orgId
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
? eq(deploymentsTable.org_id, orgId)
|
||||||
orgId
|
: and(
|
||||||
? eq(deploymentsTable.org_id, orgId)
|
eq(deploymentsTable.user_id, userId),
|
||||||
: and(
|
isNull(deploymentsTable.org_id)
|
||||||
eq(deploymentsTable.user_id, userId),
|
)
|
||||||
isNull(deploymentsTable.org_id),
|
)
|
||||||
),
|
);
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!deployment) throw new Error("No deployment found");
|
if (!deployment) throw new Error("No deployment found");
|
||||||
|
|
||||||
return deployment;
|
return deployment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateSharePageInfo = withServerPromise(
|
export const updateSharePageInfo = withServerPromise(
|
||||||
async ({
|
async ({
|
||||||
id,
|
id,
|
||||||
...data
|
...data
|
||||||
}: z.infer<typeof publicShareDeployment> & {
|
}: z.infer<typeof publicShareDeployment> & {
|
||||||
id: string;
|
id: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { userId } = auth();
|
const { userId } = auth();
|
||||||
if (!userId) return { error: "No user id" };
|
if (!userId) return { error: "No user id" };
|
||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
const [deployment] = await db
|
const [deployment] = await db
|
||||||
.update(deploymentsTable)
|
.update(deploymentsTable)
|
||||||
.set(data)
|
.set(data)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, id),
|
eq(deploymentsTable.id, id)
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return { message: "Info Updated" };
|
return { message: "Info Updated" };
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,42 +16,32 @@ export async function findAllRuns({
|
|||||||
offset = 0,
|
offset = 0,
|
||||||
}: RunsSearchTypes) {
|
}: RunsSearchTypes) {
|
||||||
return await db.query.workflowRunsTable.findMany({
|
return await db.query.workflowRunsTable.findMany({
|
||||||
where: eq(workflowRunsTable.workflow_id, workflow_id),
|
where: eq(workflowRunsTable.workflow_id, workflow_id),
|
||||||
orderBy: desc(workflowRunsTable.created_at),
|
orderBy: desc(workflowRunsTable.created_at),
|
||||||
offset: offset,
|
offset: offset,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
extras: {
|
extras: {
|
||||||
number: sql<number>`row_number() over (order by created_at)`.as(
|
number: sql<number>`row_number() over (order by created_at)`.as("number"),
|
||||||
"number",
|
total: sql<number>`count(*) over ()`.as("total"),
|
||||||
),
|
duration:
|
||||||
total: sql<number>`count(*) over ()`.as("total"),
|
sql<number>`(extract(epoch from ended_at) - extract(epoch from created_at))`.as(
|
||||||
duration:
|
"duration"
|
||||||
sql<number>`(extract(epoch from ended_at) - extract(epoch from created_at))`.as(
|
),
|
||||||
"duration",
|
},
|
||||||
),
|
with: {
|
||||||
cold_start_duration:
|
machine: {
|
||||||
sql<number>`(extract(epoch from started_at) - extract(epoch from created_at))`.as(
|
columns: {
|
||||||
"cold_start_duration",
|
name: true,
|
||||||
),
|
endpoint: true,
|
||||||
run_duration:
|
},
|
||||||
sql<number>`(extract(epoch from ended_at) - extract(epoch from started_at))`.as(
|
},
|
||||||
"run_duration",
|
version: {
|
||||||
),
|
columns: {
|
||||||
},
|
version: true,
|
||||||
with: {
|
},
|
||||||
machine: {
|
},
|
||||||
columns: {
|
},
|
||||||
name: true,
|
});
|
||||||
endpoint: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
version: {
|
|
||||||
columns: {
|
|
||||||
version: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findAllRunsWithCounts(props: RunsSearchTypes) {
|
export async function findAllRunsWithCounts(props: RunsSearchTypes) {
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { db } from "@/db/db";
|
||||||
|
import {
|
||||||
|
checkpointTable,
|
||||||
|
} from "@/db/schema";
|
||||||
|
import { auth } from "@clerk/nextjs";
|
||||||
|
import { and, desc, eq, isNull } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function getAllUserCheckpoints() {
|
||||||
|
const { userId, orgId } = await auth();
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkpoints = await db.query.checkpointTable.findMany({
|
||||||
|
with: {
|
||||||
|
user: {
|
||||||
|
columns: {
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
id: true,
|
||||||
|
updated_at: true,
|
||||||
|
name: true,
|
||||||
|
civitai_url: true,
|
||||||
|
civitai_model_response: true,
|
||||||
|
is_public: true,
|
||||||
|
},
|
||||||
|
orderBy: desc(checkpointTable.updated_at),
|
||||||
|
where:
|
||||||
|
orgId != undefined
|
||||||
|
? eq(checkpointTable.org_id, orgId)
|
||||||
|
: and(eq(checkpointTable.user_id, userId), isNull(checkpointTable.org_id)),
|
||||||
|
});
|
||||||
|
|
||||||
|
return checkpoints;
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
"use server";
|
|
||||||
|
|
||||||
import { LemonSqueezy } from "@lemonsqueezy/lemonsqueezy.js";
|
|
||||||
import "server-only";
|
|
||||||
|
|
||||||
const ls = new LemonSqueezy(process.env.LEMONSQUEEZY_API_KEY || "");
|
|
||||||
|
|
||||||
export async function getPricing() {
|
|
||||||
const products = await ls.getProducts();
|
|
||||||
|
|
||||||
return products;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getUsage() {
|
|
||||||
const usageRecord = await ls.getUsageRecords();
|
|
||||||
|
|
||||||
return usageRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setUsage(id: number, quantity: number) {
|
|
||||||
const setUsage = await ls.createUsageRecord({
|
|
||||||
subscriptionItemId: id,
|
|
||||||
quantity: quantity,
|
|
||||||
});
|
|
||||||
|
|
||||||
return setUsage;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSubscription() {
|
|
||||||
const subscription = await ls.getSubscriptions();
|
|
||||||
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSubscriptionItem() {
|
|
||||||
const subscriptionItem = await ls.getSubscriptionItems();
|
|
||||||
|
|
||||||
return subscriptionItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getUserData() {
|
|
||||||
const user = await ls.getUser();
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
// from chatgpt https://chat.openai.com/share/4985d20b-30b1-4a28-87f6-6ebf84a1040e
|
||||||
|
|
||||||
|
export const creatorSchema = z.object({
|
||||||
|
username: z.string().optional(),
|
||||||
|
image: z.string().url().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fileMetadataSchema = z.object({
|
||||||
|
fp: z.string().optional(),
|
||||||
|
size: z.string().optional(),
|
||||||
|
format: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fileSchema = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
sizeKB: z.number().optional(),
|
||||||
|
name: z.string(),
|
||||||
|
type: z.string().optional(),
|
||||||
|
metadata: fileMetadataSchema.optional(),
|
||||||
|
pickleScanResult: z.string().optional(),
|
||||||
|
pickleScanMessage: z.string().nullable(),
|
||||||
|
virusScanResult: z.string().optional(),
|
||||||
|
virusScanMessage: z.string().nullable(),
|
||||||
|
scannedAt: z.string().optional(),
|
||||||
|
hashes: z.record(z.string()).optional(),
|
||||||
|
downloadUrl: z.string().url(),
|
||||||
|
primary: z.boolean().optional().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const imageMetadataSchema = z.object({
|
||||||
|
hash: z.string(),
|
||||||
|
width: z.number(),
|
||||||
|
height: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const imageMetaSchema = z.object({
|
||||||
|
ENSD: z.string().optional(),
|
||||||
|
Size: z.string().optional(),
|
||||||
|
seed: z.number().optional(),
|
||||||
|
Model: z.string().optional(),
|
||||||
|
steps: z.number().optional(),
|
||||||
|
hashes: z.record(z.string()).optional(),
|
||||||
|
prompt: z.string().optional(),
|
||||||
|
sampler: z.string().optional(),
|
||||||
|
cfgScale: z.number().optional(),
|
||||||
|
ClipSkip: z.number().optional(),
|
||||||
|
resources: z.array(
|
||||||
|
z.object({
|
||||||
|
hash: z.string().optional(),
|
||||||
|
name: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
weight: z.number().optional(),
|
||||||
|
})
|
||||||
|
).optional(),
|
||||||
|
ModelHash: z.string().optional(),
|
||||||
|
HiresSteps: z.string().optional(),
|
||||||
|
HiresUpscale: z.string().optional(),
|
||||||
|
HiresUpscaler: z.string().optional(),
|
||||||
|
negativePrompt: z.string(),
|
||||||
|
DenoisingStrength: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const imageSchema = z.object({
|
||||||
|
url: z.string().url().optional(),
|
||||||
|
nsfw: z.enum(["None", "Soft", "Mature"]).optional(),
|
||||||
|
width: z.number().optional(),
|
||||||
|
height: z.number().optional(),
|
||||||
|
hash: z.string().optional(),
|
||||||
|
type: z.string().optional(),
|
||||||
|
metadata: imageMetadataSchema.optional(),
|
||||||
|
meta: imageMetaSchema.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const modelVersionSchema = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
modelId: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
createdAt: z.string().optional(),
|
||||||
|
updatedAt: z.string().optional(),
|
||||||
|
status: z.enum(["Published", "Unpublished"]).optional(),
|
||||||
|
publishedAt: z.string().optional(),
|
||||||
|
trainedWords: z.array(z.string()).nullable(),
|
||||||
|
trainingStatus: z.string().nullable(),
|
||||||
|
trainingDetails: z.string().nullable(),
|
||||||
|
baseModel: z.string().optional(),
|
||||||
|
baseModelType: z.string().optional(),
|
||||||
|
earlyAccessTimeFrame: z.number().optional(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
vaeId: z.string().nullable(),
|
||||||
|
stats: z.object({
|
||||||
|
downloadCount: z.number(),
|
||||||
|
ratingCount: z.number(),
|
||||||
|
rating: z.number(),
|
||||||
|
}).optional(),
|
||||||
|
files: z.array(fileSchema),
|
||||||
|
images: z.array(imageSchema),
|
||||||
|
downloadUrl: z.string().url(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const statsSchema = z.object({
|
||||||
|
downloadCount: z.number(),
|
||||||
|
favoriteCount: z.number(),
|
||||||
|
commentCount: z.number(),
|
||||||
|
ratingCount: z.number(),
|
||||||
|
rating: z.number(),
|
||||||
|
tippedAmountCount: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CivitaiModelResponse = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
type: z.enum(["Checkpoint", "Lora"]),
|
||||||
|
poi: z.boolean().optional(),
|
||||||
|
nsfw: z.boolean().optional(),
|
||||||
|
allowNoCredit: z.boolean().optional(),
|
||||||
|
allowCommercialUse: z.enum(["Rent"]).optional(),
|
||||||
|
allowDerivatives: z.boolean().optional(),
|
||||||
|
allowDifferentLicense: z.boolean().optional(),
|
||||||
|
stats: statsSchema.optional(),
|
||||||
|
creator: creatorSchema.optional(),
|
||||||
|
tags: z.array(z.string()).optional(),
|
||||||
|
modelVersions: z.array(modelVersionSchema),
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user