From b1e9bcc4e648350143044e46ba2ee4a1155bdc3a Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 01:58:00 -0800 Subject: [PATCH 01/11] blah --- builder/modal-builder/src/main.py | 103 +- builder/modal-builder/src/template/app.py | 9 +- builder/modal-builder/src/template/config.py | 8 +- .../src/template/data/extra_model_paths.yaml | 26 +- .../src/template/data/insert_models.py | 101 ++ .../src/template/data/install_deps.py | 10 +- .../src/template/volume_setup.py | 9 + .../modal-builder/src/volume-builder/app.py | 69 ++ .../src/volume-builder/config.py | 17 + web/bun.lockb | Bin 498007 -> 498455 bytes web/drizzle/0035_known_skin.sql | 64 + web/drizzle/meta/0035_snapshot.json | 1090 +++++++++++++++++ web/drizzle/meta/_journal.json | 7 + web/src/app/(app)/api/volume-upload/route.ts | 53 + web/src/app/(app)/storage/loading.tsx | 9 + web/src/app/(app)/storage/page.tsx | 35 + web/src/components/CheckpointList.tsx | 341 ++++++ web/src/components/NavbarMenu.tsx | 8 +- .../custom-form/checkpoint-input.tsx | 86 ++ web/src/db/schema.ts | 116 +- web/src/server/addCheckpointSchema.tsx | 5 + web/src/server/curdCheckpoint.ts | 252 ++++ web/src/server/curdMachine.ts | 3 + web/src/server/getAllUserCheckpoints.tsx | 41 + web/src/types/civitai.ts | 129 ++ 25 files changed, 2561 insertions(+), 30 deletions(-) create mode 100644 builder/modal-builder/src/template/data/insert_models.py create mode 100644 builder/modal-builder/src/template/volume_setup.py create mode 100644 builder/modal-builder/src/volume-builder/app.py create mode 100644 builder/modal-builder/src/volume-builder/config.py create mode 100644 web/drizzle/0035_known_skin.sql create mode 100644 web/drizzle/meta/0035_snapshot.json create mode 100644 web/src/app/(app)/api/volume-upload/route.ts create mode 100644 web/src/app/(app)/storage/loading.tsx create mode 100644 web/src/app/(app)/storage/page.tsx create mode 100644 web/src/components/CheckpointList.tsx create mode 100644 web/src/components/custom-form/checkpoint-input.tsx create mode 100644 web/src/server/addCheckpointSchema.tsx create mode 100644 web/src/server/curdCheckpoint.ts create mode 100644 web/src/server/getAllUserCheckpoints.tsx create mode 100644 web/src/types/civitai.ts diff --git a/builder/modal-builder/src/main.py b/builder/modal-builder/src/main.py index 2493a37..b907fc4 100644 --- a/builder/modal-builder/src/main.py +++ b/builder/modal-builder/src/main.py @@ -8,6 +8,7 @@ from enum import Enum import json import subprocess import time +from uuid import uuid4 from contextlib import asynccontextmanager import asyncio import threading @@ -19,6 +20,7 @@ from urllib.parse import parse_qs from starlette.middleware.base import BaseHTTPMiddleware from starlette.types import ASGIApp, Scope, Receive, Send + from concurrent.futures import ThreadPoolExecutor # executor = ThreadPoolExecutor(max_workers=5) @@ -174,6 +176,7 @@ class Item(BaseModel): snapshot: Snapshot models: List[Model] callback_url: str + checkpoint_volume_name: str gpu: GPUType = Field(default=GPUType.T4) @field_validator('gpu') @@ -223,6 +226,102 @@ async def websocket_endpoint(websocket: WebSocket, machine_id: str): # return {"Hello": "World"} +class UploadType(str, Enum): + checkpoint = "checkpoint" + +class UploadBody(BaseModel): + download_url: str + volume_name: str + volume_id: str + checkpoint_id: str + upload_type: UploadType + callback_url: str + + +UPLOAD_TYPE_DIR_MAP = { + UploadType.checkpoint: "checkpoints" +} + +@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}") + + asyncio.create_task(upload_logic(body)) + + # check that this + return JSONResponse(status_code=200, content={"message": "Volume uploading", "build_machine_instance_id": fly_instance_id}) + +async def upload_logic(body: UploadBody): + folder_path = f"/app/builds/{body.volume_id}" + + cp_process = await asyncio.subprocess.create_subprocess_exec("cp", "-r", "/app/src/volume-builder", folder_path) + await cp_process.wait() + + upload_path = UPLOAD_TYPE_DIR_MAP[body.upload_type] + config = { + "volume_names": { + body.volume_name: {"download_url": body.download_url, "folder_path": upload_path} + }, + "volume_paths": { + body.volume_name: f'/volumes/{uuid4()}' + }, + "callback_url": body.callback_url, + "callback_body": { + "checkpoint_id": body.checkpoint_id, + "volume_id": body.volume_id, + "folder_path": upload_path, + } + } + with open(f"{folder_path}/config.py", "w") as f: + f.write("config = " + json.dumps(config)) + + process = 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"} + ) + + # error_logs = [] + # async def read_stream(stream): + # while True: + # line = await stream.readline() + # if line: + # l = line.decode('utf-8').strip() + # error_logs.append(l) + # logger.error(l) + # error_logs.append({ + # "logs": l, + # "timestamp": time.time() + # }) + # else: + # break + + # stderr_read_task = asyncio.create_task(read_stream(process.stderr)) + # + # await asyncio.wait([stderr_read_task]) + # await process.wait() + + # if process.returncode != 0: + # error_logs.append({"logs": "Unable to upload volume.", "timestamp": time.time()}) + # # Error handling: send POST request to callback URL with error details + # requests.post(body.callback_url, json={ + # "volume_id": body.volume_id, + # "checkpoint_id": body.checkpoint_id, + # "folder_path": upload_path, + # "error_logs": json.dumps(error_logs), + # "status": "failed" + # }) + # + # requests.post(body.callback_url, json={ + # "checkpoint_id": body.checkpoint_id, + # "volume_id": body.volume_id, + # "folder_path": upload_path, + # "status": "success" + # }) @app.post("/create") async def create_machine(item: Item): @@ -312,7 +411,9 @@ async def build_logic(item: Item): config = { "name": item.name, "deploy_test": os.environ.get("DEPLOY_TEST_FLAG", "False"), - "gpu": item.gpu + "gpu": item.gpu, + "public_checkpoint_volume": "model-store", + "private_checkpoint_volume": item.checkpoint_volume_name } with open(f"{folder_path}/config.py", "w") as f: f.write("config = " + json.dumps(config)) diff --git a/builder/modal-builder/src/template/app.py b/builder/modal-builder/src/template/app.py index 1d4145d..1650252 100644 --- a/builder/modal-builder/src/template/app.py +++ b/builder/modal-builder/src/template/app.py @@ -7,6 +7,7 @@ import urllib.parse from pydantic import BaseModel from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse +from volume_setup import volumes # deploy_test = False @@ -27,6 +28,7 @@ deploy_test = config["deploy_test"] == "True" web_app = FastAPI() print(config) print("deploy_test ", deploy_test) +print('volumes', volumes) stub = Stub(name=config["name"]) # print(stub.app_id) @@ -56,7 +58,7 @@ if not deploy_test: # # Install comfy deploy # "cd /comfyui/custom_nodes && git clone https://github.com/BennyKok/comfyui-deploy.git", # ) - # .copy_local_file(f"{current_directory}/data/extra_model_paths.yaml", "/comfyui") + .copy_local_file(f"{current_directory}/data/extra_model_paths.yaml", "/comfyui") .copy_local_file(f"{current_directory}/data/start.sh", "/start.sh") .run_commands("chmod +x /start.sh") @@ -154,7 +156,7 @@ image = Image.debian_slim() 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): import subprocess import time @@ -235,7 +237,7 @@ async def bar(request_input: RequestInput): # pass -@stub.function(image=image) +@stub.function(image=image, volumes=volumes) @asgi_app() def comfyui_api(): return web_app @@ -284,6 +286,7 @@ def spawn_comfyui_in_background(): # Restrict to 1 container because we want to our ComfyUI session state # to be on a single container. concurrency_limit=1, + volumes=volumes, timeout=10 * 60, ) @asgi_app() diff --git a/builder/modal-builder/src/template/config.py b/builder/modal-builder/src/template/config.py index e59020b..a651642 100644 --- a/builder/modal-builder/src/template/config.py +++ b/builder/modal-builder/src/template/config.py @@ -1 +1,7 @@ -config = {"name": "my-app", "deploy_test": "True", "gpu": "T4"} \ No newline at end of file +config = { + "name": "my-app", + "deploy_test": "True", + "gpu": "T4", + "public_checkpoint_volume": "model-store", + "private_checkpoint_volume": "private-model-store" +} diff --git a/builder/modal-builder/src/template/data/extra_model_paths.yaml b/builder/modal-builder/src/template/data/extra_model_paths.yaml index 6e07d7b..66f758e 100644 --- a/builder/modal-builder/src/template/data/extra_model_paths.yaml +++ b/builder/modal-builder/src/template/data/extra_model_paths.yaml @@ -1,11 +1,15 @@ -comfyui: - base_path: /runpod-volume/ComfyUI/ - checkpoints: models/checkpoints/ - clip: models/clip/ - clip_vision: models/clip_vision/ - configs: models/configs/ - controlnet: models/controlnet/ - embeddings: models/embeddings/ - loras: models/loras/ - upscale_models: models/upscale_models/ - vae: models/vae/ \ No newline at end of file +public: + base_path: /public_models/ + checkpoints: checkpoints + clip: clip + clip_vision: clip_vision + configs: configs + controlnet: controlnet + embeddings: embeddings + loras: loras + upscale_models: upscale_models + vae: vae + +private: + base_path: /private_models/ + checkpoints: checkpoints diff --git a/builder/modal-builder/src/template/data/insert_models.py b/builder/modal-builder/src/template/data/insert_models.py new file mode 100644 index 0000000..a189bdd --- /dev/null +++ b/builder/modal-builder/src/template/data/insert_models.py @@ -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 diff --git a/builder/modal-builder/src/template/data/install_deps.py b/builder/modal-builder/src/template/data/install_deps.py index 3ff3ca3..2b8f8d1 100644 --- a/builder/modal-builder/src/template/data/install_deps.py +++ b/builder/modal-builder/src/template/data/install_deps.py @@ -45,12 +45,12 @@ for package in packages: response = requests.request("POST", f"{root_url}/customnode/install", json=package, headers=headers) print(response.text) -with open('models.json') as f: - models = json.load(f) +# with open('models.json') as f: +# models = json.load(f) -for model in models: - response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers) - print(response.text) +# for model in models: +# response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers) +# print(response.text) # Close the server server_process.terminate() diff --git a/builder/modal-builder/src/template/volume_setup.py b/builder/modal-builder/src/template/volume_setup.py new file mode 100644 index 0000000..fb50e9f --- /dev/null +++ b/builder/modal-builder/src/template/volume_setup.py @@ -0,0 +1,9 @@ +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"]) + +PUBLIC_BASEMODEL_DIR = "/public_models" +PRIVATE_BASEMODEL_DIR = "/private_models" +volumes = {PUBLIC_BASEMODEL_DIR: public_model_volume, PRIVATE_BASEMODEL_DIR: private_volume} diff --git a/builder/modal-builder/src/volume-builder/app.py b/builder/modal-builder/src/volume-builder/app.py new file mode 100644 index 0000000..3a7e779 --- /dev/null +++ b/builder/modal-builder/src/volume-builder/app.py @@ -0,0 +1,69 @@ +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["volume_paths"] +callback_url = config["callback_url"] +callback_body = config["callback_body"] + +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) + +# download config { "download_url": "", "folder_path": ""} +timeout=5000 +@stub.function(volumes=volumes, image=image, timeout=timeout, gpu=None) +def download_model(volume_name, download_config): + import requests + download_url = download_config["download_url"] + folder_path = download_config["folder_path"] + volume_base_path = vol_name_to_path[volume_name] + model_store_path = os.path.join(volume_base_path, folder_path) + + subprocess.run(["wget", download_url, "--content-disposition", "-P", model_store_path]) + subprocess.run(["ls", "-la", volume_base_path]) + subprocess.run(["ls", "-la", model_store_path]) + volumes[volume_base_path].commit() + + status = {"status": "success"} + requests.post(callback_url, json={**status, **callback_body}) + + +@stub.local_entrypoint() +def simple_download(): + import requests + print(vol_name_to_links) + print([(vol_name, link) for vol_name,link in vol_name_to_links.items()]) + try: + list(download_model.starmap([(vol_name, link) for vol_name,link in vol_name_to_links.items()])) + except modal.exception.FunctionTimeoutError as e: + status = {"status": "failed", "error_logs": f"{str(e)}", "timeout": timeout} + requests.post(callback_url, json={**status, **callback_body}) + except Exception as e: + status = {"status": "failed", "error_logs": str(e)} + requests.post(callback_url, json={**status, **callback_body}) + diff --git a/builder/modal-builder/src/volume-builder/config.py b/builder/modal-builder/src/volume-builder/config.py new file mode 100644 index 0000000..2bf9032 --- /dev/null +++ b/builder/modal-builder/src/volume-builder/config.py @@ -0,0 +1,17 @@ +config = { + "volume_names": { + "test": { + "download_url": "https://pub-6230db03dc3a4861a9c3e55145ceda44.r2.dev/openpose-pose (1).png", + "folder_path": "images" + } + }, + "volume_paths": { + "test": "/volumes/something" + }, + "callback_url": "", + "callback_body": { + "checkpoint_id": "", + "volume_id": "", + "folder_path": "images", + } +} diff --git a/web/bun.lockb b/web/bun.lockb index 650dd43bf9f70e00173efe1b412186dd13171118..cf2f39b4ec09dbc556898cd4d51ab62c0c0c16fc 100755 GIT binary patch delta 15889 zcmeI3Yiv}<6~|}oiw!o0%c~Gxb<8t7jCm!205OoKV-qLAkObp;y)3rDUOT&XOrhyG z9|-9~4Jm;k5C~R*@^B~+T1!zYQ9z>VD^lBpk|q*SX(d|Px-IR8DysVb?>#mc!Qv%* z_a>@GoVjOzdv?yunKQGO>)CV1N1Gpv-n=Ab(~cL5Hx~Og7N>l_IJNH+`bdeoTKX4# z!WY9WrFscHzgE57U6umFqBN!YD%BjQsm@&!aT)gkPTW^Nfby)t;~TG?Tw7fbFmXCE~e)-lD_!7^c$t+@;KhVVN1 zK4%w&nk=Thax8wagYkPKi6ibhVD6wEhF?YJ--CJYh=TSu{A%v)hu_Q#U_IssxN=e1 zkt%=mZYOt|tn6>{{mp#;|Dv*Yj&;5ZPZTRP0E^0$S0VtbDiMH=$~H&IPLOp}_O>b@ zs|x70DqHiOGb`vgABrn?SFY$JF&(`F@W8S7#U7I65qBLh_pt7Sn$xrss72 z@xBS_r)E0Q4aV7?GyjPgNt}))f{4ExVeNKjIRA;9&u%*wzdBKg6t83PtGx}sLj2td zKbNdOP!|s2I{F2r`pA;)!r;Fd@88DEdhne;!jQ&a!M&D7{CZEre9^kX2Sd|^~nUk%3=)y;>N)17fuYHJ+NEX9~cGWTC z8+#zju1-X3!}#n>>v~Mi(e;>|n^kyd8GZb$&Kz?Vp()&uI>oHofY0<)Ju&!v=7Bwt z`&?77qTU}^t5#ohp2u??UeC;)m#{?kzJzq|sge-`=OLRv(h#l=RjL91b^heSsYw#k zUO5)OxXIxk1Cu!7t^0Avz^3T)o`*i~Me%@uRURUoAM}pziIlkzr z2cVctch}G*yI?f@@%B32&X{>yabFqF5wsp-+=F)p?1l&Oa1>BkkZTWXxx~62$aBd_ zjDg55k$lI_?$noeiAf6e4WpoA%`vx~V5FuxghyoO3w)g?KN85CNAU@4uo-=hqtn{e z(cQW;+MTf8+Oha${>2flghy-kZ6%kvyc@fI`)=%H390&pt82sl)k?`hpapwUb2Rk; zPD67Qy!@;$`pEuOT(MU~!|O7ee?uqiOl@PN!5^p!o+-WBnd_Mu*@6gVw;)2FqOzOn z!fS)!RjO#EFZ#b5Q+yE3E_qZLl3K~+g&D_wPLN?WmH%3F~dtoqp-otcm`!iQRMn)oQ*&`W1tdPQ|zEzZZOb6;~l1l27zBYr35QfunsE#=F5Z^Qa*GEx|RqoIBxY!p~3UUQvS3d93ui=cW0V>ra2Vhw7_#xP!tQ|nB7|j}u;I`*M~PD7h2h5u z>NQ~#z!I7w21i4-3!E(Mi(u~vn<6Y5>?EffWhxk*+zI)y zuxVnK19nPSp4g26J1uO6*y*u=X9Ug^I1cPAST9^>fpK=mL;l0uL%Cn?T1!ZDIb|_~U>K`DBP+44=f6qhnrI2wRFPqlMpZD8A1G zEfJOry#P8LF2j}WPJ=uQoe9cz$?_na3MOcch509(4oPFnC{1{i>I_JRuytVU%uGl* zKY+4c>}ElK7dgWO-2ld9Hc{c8kD!p|{9~UlTS5 z%s`lATZQ37yRyY@o3Q)91_(34xZ(#2+b(QASRuB9X#T%0Z~HacIQ|B_VEL}V$Dlti>;SHGTnJ$fupAP* zBIwKkmczo9KxYoHwBpLoJ`Q0Nn4m|*j=Lb)G{*mX0-q4b3_U9BN$3aI3d%8IPeHF0 zb{vfAQb+)a$pk$C#w09(?8Uq@pMENzT}B3BKK%?Vdaz?TUTi`5IX6ES!>6GSm3jJw zuxG%SXe_@J_AGQJS^=)_;>u;b0`fL=M)Fr;R|@?gbe3NWTgm)mI9Yxpu#7K+y(jET zK%3bz%KO5~p+6|>oG?Gw5rl(L`>n7F=yejU^I-U=0+4#Jr#Sx~fpBA61(_&o`}bf> z%phbRLdZ~F6t)_AyF}&>!YaX#&59BFBd+XM6@<%;5xOLH)zG=J$o@pe`CkJ$j^42M ze-^{F(AR@i;CfkD4fIAZ_Wm!zYN6*#r~fJ}1U6aN--OkHUBW`$l<1mq>!c+Ig{B#6Euh1 z*k}S_qXMR;p0bNKX6&sVC?}_$^re{|eDKMq{@>dF@IdG2vFkLm*UWd;w}1OvYkm7$ z|GoE_bIfyNuALs!yei^q>z@3*`HsE$5ohzA@dduMo?Kt4C&}0BG0eUKr`!Clz-jo3 zJsFy0tac{(iafIsl1-cBFnnce67ktyHzR=mem+>Ar6;>xIAF3=4O@!(VS)xX?}A`c z35(Hz^~L=oLk0d7Zl%|^F6bgdbOya8I9cqOb&o2H)q#cU`)A42;Pzb*EXRaMLn#&Q zD_b6Jg@er^EXOP?*K6nMbvvzt!2Jw0GF+anC-znata2vsz^D#dr15%KUQK^tLa*24 zi8T8*g%NmHq@gFXVkYU;TWbH-N$@2((o=kIoA z-RI5;oaz7WC(A~;cT{YwD=XiuDsL+FkX1+Px!d{HfD--NS8Hjm<(>aDiEuA^l-9hr zC1kR^gZ}v93U`G+6K6fJ;~(IGZM|RLJQ(si?wy7_6OF?d9QyT>s4JD%=1hsf%S5*b z^Sw4_a=;5+ze(L&8n)M$l~-5vmN-;=pfGdVL#Ai#Lw!fcfWi4&?}-N(5IBML#_#?Z zPmO`!MgoIE`%B(tr@Id#xi0ymzO z(Z1{zX@hv^l*50Q*>l*nvNd39z}A4R0b2vM25b%38n88BYrxiktpQsDwgzks*c$k! zG>{p0t4aQbNy zIb;dusb(XwaTrg<0WajKL^Awnsg{x@fknW_za8pz2}T>iIQ;6auEc7jlF5eR@Efv` zU``ynVO3rE+K)3>*dDB+Ddw7_60S0@-M=^JEjrQot_C?x1CX$r`D! zOJtK988`;;GT{^kjs?3yHjQye5SNmfJl zgy_#f(QG2j1^a<}yqRnk*fp{j$({tOCfh#X)k<8R0?7XF zCtMEShZ0J8O|lj6lUeFkvXvZzP8}dy1z*NUH9kNucg{1gPvI{>e~5AU@Q=aQ`u~uy z0LRA#q8%n%EeB+6WNUz$WoEQ?vO@Ta$U4Z1z)qktQnioBo`t`a)#?P3>ZoE^9iWu* zD1bam*21!QE1uxs=fIAjgi^{+$exFPiCeeFf=wYiPgV~0i@s&C|6dS# z0cByi$}h<(;7itWMPHFs!k46leN9#c{~&z1$_r%GWHQG^vKshxqHeTH!sHoTEi0ha zb(wQ}jB)0#O-8i8)F0 statement-breakpoint +DO $$ BEGIN + CREATE TYPE "resource_upload" AS ENUM('started', 'success', 'failed'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoints" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" text, + "org_id" text, + "description" text, + "checkpoint_volume_id" uuid NOT NULL, + "model_name" text, + "folder_path" text, + "civitai_id" text, + "civitai_version_id" text, + "civitai_url" text, + "civitai_download_url" text, + "civitai_model_response" jsonb, + "hf_url" text, + "s3_url" text, + "client_url" text, + "is_public" boolean DEFAULT false NOT NULL, + "status" "resource_upload" DEFAULT 'started' NOT NULL, + "upload_machine_id" text, + "upload_type" "model_upload_type" NOT NULL, + "error_log" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoint_volume" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" text, + "org_id" text, + "volume_name" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "disabled" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_checkpoint_volume_id_checkpoint_volume_id_fk" FOREIGN KEY ("checkpoint_volume_id") REFERENCES "comfyui_deploy"."checkpoint_volume"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "comfyui_deploy"."checkpoint_volume" ADD CONSTRAINT "checkpoint_volume_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/web/drizzle/meta/0035_snapshot.json b/web/drizzle/meta/0035_snapshot.json new file mode 100644 index 0000000..f6a7956 --- /dev/null +++ b/web/drizzle/meta/0035_snapshot.json @@ -0,0 +1,1090 @@ +{ + "id": "da3d957a-757f-4677-bf78-391abe6b3652", + "prevId": "8d654f92-7f7e-420f-bbd3-73b6b27adf35", + "version": "5", + "dialect": "pg", + "tables": { + "api_keys": { + "name": "api_keys", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + } + }, + "auth_requests": { + "name": "auth_requests", + "schema": "comfyui_deploy", + "columns": { + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_hash": { + "name": "api_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expired_date": { + "name": "expired_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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 + }, + "folder_path": { + "name": "folder_path", + "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 + }, + "error_log": { + "name": "error_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_checkpoint_volume_id_fk": { + "name": "checkpoints_checkpoint_volume_id_checkpoint_volume_id_fk", + "tableFrom": "checkpoints", + "tableTo": "checkpoint_volume", + "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": { + "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": {} + }, + "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" + } + }, + "model_upload_type": { + "name": "model_upload_type", + "values": { + "civitai": "civitai", + "huggingface": "huggingface", + "other": "other" + } + }, + "resource_upload": { + "name": "resource_upload", + "values": { + "started": "started", + "success": "success", + "failed": "failed" + } + }, + "workflow_run_origin": { + "name": "workflow_run_origin", + "values": { + "manual": "manual", + "api": "api", + "public-share": "public-share" + } + }, + "workflow_run_status": { + "name": "workflow_run_status", + "values": { + "not-started": "not-started", + "running": "running", + "uploading": "uploading", + "success": "success", + "failed": "failed" + } + } + }, + "schemas": { + "comfyui_deploy": "comfyui_deploy" + }, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/web/drizzle/meta/_journal.json b/web/drizzle/meta/_journal.json index 3fbae3d..71359e4 100644 --- a/web/drizzle/meta/_journal.json +++ b/web/drizzle/meta/_journal.json @@ -246,6 +246,13 @@ "when": 1705902960991, "tag": "0034_even_lady_ursula", "breakpoints": true + }, + { + "idx": 35, + "version": "5", + "when": 1706085876992, + "tag": "0035_known_skin", + "breakpoints": true } ] } \ No newline at end of file diff --git a/web/src/app/(app)/api/volume-upload/route.ts b/web/src/app/(app)/api/volume-upload/route.ts new file mode 100644 index 0000000..f7cb3cb --- /dev/null +++ b/web/src/app/(app)/api/volume-upload/route.ts @@ -0,0 +1,53 @@ +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({ + volume_id: z.string(), + checkpoint_id: z.string(), + folder_path: z.string().optional(), + status: z.enum(['success', 'failed']), + error_log: z.string().optional(), + timeout: z.number().optional(), +}); + +export async function POST(request: Request) { + const [data, error] = await parseDataSafe(Request, request); + if (!data || error) return error; + + const { checkpoint_id, error_log, status, folder_path } = data; + + if (status === "success") { + await db + .update(checkpointTable) + .set({ + status: "success", + folder_path + // build_log: build_log, + }) + .where(eq(checkpointTable.id, checkpoint_id)); + } else { + // console.log(data); + await db + .update(checkpointTable) + .set({ + status: "failed", + error_log, + // status: "error", + // build_log: build_log, + }) + .where(eq(checkpointTable.id, checkpoint_id)); + } + + return NextResponse.json( + { + message: "success", + }, + { + status: 200, + } + ); +} diff --git a/web/src/app/(app)/storage/loading.tsx b/web/src/app/(app)/storage/loading.tsx new file mode 100644 index 0000000..9ff4783 --- /dev/null +++ b/web/src/app/(app)/storage/loading.tsx @@ -0,0 +1,9 @@ +"use client"; + +import { LoadingPageWrapper } from "@/components/LoadingWrapper"; +import { usePathname } from "next/navigation"; + +export default function Loading() { + const pathName = usePathname(); + return ; +} diff --git a/web/src/app/(app)/storage/page.tsx b/web/src/app/(app)/storage/page.tsx new file mode 100644 index 0000000..c17cc9b --- /dev/null +++ b/web/src/app/(app)/storage/page.tsx @@ -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 ; +} + +async function CheckpointListServer() { + const { userId } = auth(); + + if (!userId) { + return
No auth
; + } + + const user = await clerkClient.users.getUser(userId); + + if (!user) { + await setInitialUserData(userId); + } + + const checkpoints = await getAllUserCheckpoints(); + + if (!checkpoints) { + return
No checkpoints found
; + } + + return ( +
+ +
+ ); +} diff --git a/web/src/components/CheckpointList.tsx b/web/src/components/CheckpointList.tsx new file mode 100644 index 0000000..48a2c81 --- /dev/null +++ b/web/src/components/CheckpointList.tsx @@ -0,0 +1,341 @@ +"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 } from "lucide-react"; +import * as React from "react"; +import { addCivitaiCheckpoint } from "@/server/curdCheckpoint"; +import { addCivitaiCheckpointSchema } from "@/server/addCheckpointSchema"; + +export type CheckpointItemList = NonNullable< + Awaited> +>[0]; + +export const columns: ColumnDef[] = [ + { + accessorKey: "id", + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "model_name", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const checkpoint = row.original; + console.log(checkpoint); + return ( + + + {row.original.model_name} + + + {checkpoint.is_public ? ( + Public + ) : ( + Private + )} + + ); + }, + }, + { + accessorKey: "status", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + return ( + + {row.original.status} + + ); + }, + }, + { + accessorKey: "upload_type", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + return {row.original.upload_type}; + }, + }, + { + accessorKey: "date", + sortingFn: "datetime", + enableSorting: true, + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => ( +
+ {getRelativeTime(row.original.updated_at)} +
+ ), + }, + // { + // id: "actions", + // enableHiding: false, + // cell: ({ row }) => { + // const checkpoint = row.original; + // + // return ( + // + // + // + // + // + // Actions + // { + // deleteWorkflow(checkpoint.id); + // }} + // > + // Delete Workflow + // + // + // + // ); + // }, + // }, +]; + +export function CheckpointList({ data }: { data: CheckpointItemList[] }) { + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + 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 ( +
+
+ + table.getColumn("name")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> +
+ + Pick a checkpoint from{" "} + + civitai.com + {" "} + and place it's url here + + ), + }, + }} + /> +
+
+ + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+ + +
+
+
+ ); +} diff --git a/web/src/components/NavbarMenu.tsx b/web/src/components/NavbarMenu.tsx index 74cc942..81345c6 100644 --- a/web/src/components/NavbarMenu.tsx +++ b/web/src/components/NavbarMenu.tsx @@ -34,6 +34,10 @@ export function NavbarMenu({ className }: { className?: string }) { name: "API Keys", path: "/api-keys", }, + { + name: "Storage", + path: "/storage", + }, ]; return ( @@ -42,9 +46,9 @@ export function NavbarMenu({ className }: { className?: string }) { {isDesktop && ( - + {pages.map((page) => ( >(); + const [modelVersionid, setModelVersionId] = React.useState(); + 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 = CivitaiModelResponse.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) => { + // handleSearch(event.target.value); + // }, + }; + + return ( + + {fieldConfigItem.inputProps?.showLabel && ( + + {label} + {isRequired && *} + + )} + + + + + ); +} diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts index 236983f..dd96a9c 100644 --- a/web/src/db/schema.ts +++ b/web/src/db/schema.ts @@ -1,3 +1,4 @@ +import { CivitaiModelResponse } from "@/types/civitai"; import { type InferSelectModel, relations } from "drizzle-orm"; import { boolean, @@ -90,7 +91,7 @@ export const workflowVersionRelations = relations( fields: [workflowVersionTable.workflow_id], references: [workflowTable.id], }), - }), + }) ); export const workflowRunStatus = pgEnum("workflow_run_status", [ @@ -139,7 +140,7 @@ export const workflowRunsTable = dbSchema.table("workflow_runs", { () => workflowVersionTable.id, { onDelete: "set null", - }, + } ), workflow_inputs: jsonb("workflow_inputs").$type>(), @@ -175,7 +176,7 @@ export const workflowRunRelations = relations( fields: [workflowRunsTable.workflow_id], references: [workflowTable.id], }), - }), + }) ); // We still want to keep the workflow run record. @@ -199,7 +200,7 @@ export const workflowOutputRelations = relations( fields: [workflowRunOutputs.run_id], references: [workflowRunsTable.id], }), - }), + }) ); // when user delete, also delete all the workflow versions @@ -232,7 +233,7 @@ export const snapshotType = z.object({ z.object({ hash: z.string(), disabled: z.boolean(), - }), + }) ), file_custom_nodes: z.array(z.any()), }); @@ -247,7 +248,7 @@ export const showcaseMedia = z.array( z.object({ url: z.string(), isCover: z.boolean().default(false), - }), + }) ); export const showcaseMediaNullable = z @@ -255,7 +256,7 @@ export const showcaseMediaNullable = z z.object({ url: z.string(), isCover: z.boolean().default(false), - }), + }) ) .nullable(); @@ -344,8 +345,109 @@ export const authRequestsTable = dbSchema.table("auth_requests", { updated_at: timestamp("updated_at").defaultNow().notNull(), }); +export const resourceUpload = pgEnum("resource_upload", [ + "started", + "success", + "failed", +]); + +export const modelUploadType = pgEnum("model_upload_type", [ + "civitai", + "huggingface", + "other", +]); + +export const checkpointTable = dbSchema.table("checkpoints", { + 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"), + description: text("description"), + + checkpoint_volume_id: uuid("checkpoint_volume_id") + .notNull() + .references(() => checkpointVolumeTable.id, { + onDelete: "cascade", + }) + .notNull(), + + model_name: text("model_name"), + folder_path: text("folder_path"), // in volume + + civitai_id: text("civitai_id"), + civitai_version_id: text("civitai_version_id"), + civitai_url: text("civitai_url"), + civitai_download_url: text("civitai_download_url"), + civitai_model_response: jsonb("civitai_model_response").$type< + z.infer + >(), + + 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(), + error_log: text("error_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").references(() => usersTable.id, { + // onDelete: "cascade", + }), + org_id: text("org_id"), + volume_name: text("volume_name").notNull(), + created_at: timestamp("created_at").defaultNow().notNull(), + updated_at: timestamp("updated_at").defaultNow().notNull(), + disabled: boolean("disabled").default(false).notNull(), +}); + +export const 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; export type WorkflowType = InferSelectModel; export type MachineType = InferSelectModel; export type WorkflowVersionType = InferSelectModel; export type DeploymentType = InferSelectModel; +export type CheckpointType = InferSelectModel; +export type CheckpointVolumeType = InferSelectModel< + typeof checkpointVolumeTable +>; diff --git a/web/src/server/addCheckpointSchema.tsx b/web/src/server/addCheckpointSchema.tsx new file mode 100644 index 0000000..b83f5a3 --- /dev/null +++ b/web/src/server/addCheckpointSchema.tsx @@ -0,0 +1,5 @@ +import { insertCivitaiCheckpointSchema } from "@/db/schema"; + +export const addCivitaiCheckpointSchema = insertCivitaiCheckpointSchema.pick({ + civitai_url: true, +}); diff --git a/web/src/server/curdCheckpoint.ts b/web/src/server/curdCheckpoint.ts new file mode 100644 index 0000000..4019b7e --- /dev/null +++ b/web/src/server/curdCheckpoint.ts @@ -0,0 +1,252 @@ +"use server"; + +import { auth } from "@clerk/nextjs"; +import { + checkpointTable, + CheckpointType, + checkpointVolumeTable, + 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 volume = await db + .select() + .from(checkpointVolumeTable) + .where( + and( + orgId + ? eq(checkpointVolumeTable.org_id, orgId) + // make sure org_id is null + : and( + eq(checkpointVolumeTable.user_id, userId), + isNull(checkpointVolumeTable.org_id), + ), + eq(checkpointVolumeTable.disabled, false), + ), + ); + return volume; +} + +export async function retrieveCheckpointVolumes() { + let volumes = await getCheckpointVolumes() + if (volumes.length === 0) { + // create volume if not already created + volumes = await addCheckpointVolume() + } + return volumes +} + +export async function addCheckpointVolume() { + const { userId, orgId } = auth(); + if (!userId) throw new Error("No user id"); + + // Insert the new checkpointVolume into the checkpointVolumeTable + const insertedVolume = await db + .insert(checkpointVolumeTable) + .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) => { + const { userId, orgId } = auth(); + console.log("1"); + + if (!data.civitai_url) return { error: "no civitai_url" }; + console.log("2"); + if (!userId) return { error: "No user id" }; + console.log("3"); + + const { url, modelVersionId } = getUrl(data?.civitai_url); + console.log("4", url, modelVersionId); + const civitaiModelRes = await fetch(url) + .then((x) => x.json()) + .then((a) => { + console.log(a) + return CivitaiModelResponse.parse(a); + }); + console.log("5"); + + if (civitaiModelRes?.modelVersions?.length === 0) { + console.log("6"); + return; // no versions to download + } + + console.log("7"); + 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(); + } + console.log("8"); + + const checkpointVolumes = await getCheckpointVolumes(); + console.log("9"); + let cVolume; + if (checkpointVolumes.length === 0) { + console.log("10"); + const volume = await addCheckpointVolume(); + console.log("11"); + cVolume = volume[0]; + } else { + console.log("12"); + cVolume = checkpointVolumes[0]; + } + + console.log("13"); + + const a = await db + .insert(checkpointTable) + .values({ + user_id: userId, + org_id: orgId, + upload_type: "civitai", + model_name: selectedModelVersion.files[0].name, + civitai_id: civitaiModelRes.id.toString(), + civitai_version_id: selectedModelVersionId, + civitai_url: data.civitai_url, + civitai_download_url: selectedModelVersion.files[0].downloadUrl, + civitai_model_response: civitaiModelRes, + checkpoint_volume_id: cVolume.id, + }) + .returning(); + console.log("14"); + + const b = a[0]; + + await uploadCheckpoint(data, b, cVolume); + console.log("15"); + // redirect(`/checkpoints/${b.id}`); + }, +); + +async function uploadCheckpoint( + data: z.infer, + c: 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: c.civitai_download_url, + volume_name: v.volume_name, + volume_id: v.id, + checkpoint_id: c.id, + callback_url: `${protocol}://${domain}/api/volume-updated`, + upload_type: "checkpoint" + }), + }, + ); + + if (!result.ok) { + const error_log = await result.text(); + await db + .update(checkpointTable) + .set({ + ...data, + status: "failed", + error_log: error_log, + }) + .where(eq(checkpointTable.id, c.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, c.id)); + } +} diff --git a/web/src/server/curdMachine.ts b/web/src/server/curdMachine.ts index b59415f..77fea3b 100644 --- a/web/src/server/curdMachine.ts +++ b/web/src/server/curdMachine.ts @@ -15,6 +15,7 @@ import { headers } from "next/headers"; import { redirect } from "next/navigation"; import "server-only"; import type { z } from "zod"; +import { retrieveCheckpointVolumes } from "./curdCheckpoint"; export async function getMachines() { const { userId, orgId } = auth(); @@ -189,6 +190,7 @@ async function _buildMachine( throw new Error("No domain"); } + const volumes = await retrieveCheckpointVolumes(); // Call remote builder const result = await fetch(`${process.env.MODAL_BUILDER_URL!}/create`, { method: "POST", @@ -202,6 +204,7 @@ async function _buildMachine( callback_url: `${protocol}://${domain}/api/machine-built`, models: data.models, //JSON.parse(data.models as string), gpu: data.gpu && data.gpu.length > 0 ? data.gpu : "T4", + checkpoint_volume_name: volumes[0].volume_name, }), }); diff --git a/web/src/server/getAllUserCheckpoints.tsx b/web/src/server/getAllUserCheckpoints.tsx new file mode 100644 index 0000000..a12a668 --- /dev/null +++ b/web/src/server/getAllUserCheckpoints.tsx @@ -0,0 +1,41 @@ +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, + model_name: true, + civitai_url: true, + civitai_model_response: true, + is_public: true, + upload_type: true, + status: 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; +} diff --git a/web/src/types/civitai.ts b/web/src/types/civitai.ts new file mode 100644 index 0000000..971ba8b --- /dev/null +++ b/web/src/types/civitai.ts @@ -0,0 +1,129 @@ +import { z } from "zod"; + +// from chatgpt https://chat.openai.com/share/4985d20b-30b1-4a28-87f6-6ebf84a1040e + +export const creatorSchema = z.object({ + username: z.string().nullish(), + image: z.string().url().nullish(), +}); + +export const fileMetadataSchema = z.object({ + fp: z.string().nullish(), + size: z.string().nullish(), + format: z.string().nullish(), +}); + +export const fileSchema = z.object({ + id: z.number(), + sizeKB: z.number().nullish(), + name: z.string(), + type: z.string().nullish(), + metadata: fileMetadataSchema.nullish(), + pickleScanResult: z.string().nullish(), + pickleScanMessage: z.string().nullable(), + virusScanResult: z.string().nullish(), + virusScanMessage: z.string().nullable(), + scannedAt: z.string().nullish(), + hashes: z.record(z.string()).nullish(), + downloadUrl: z.string().url(), + primary: z.boolean().nullish(), +}); + +export const imageMetadataSchema = z.object({ + hash: z.string(), + width: z.number(), + height: z.number(), +}); + +export const imageMetaSchema = z.object({ + ENSD: z.string().nullish(), + Size: z.string().nullish(), + seed: z.number().nullish(), + Model: z.string().nullish(), + steps: z.number().nullish(), + hashes: z.record(z.string()).nullish(), + prompt: z.string().nullish(), + sampler: z.string().nullish(), + cfgScale: z.number().nullish(), + ClipSkip: z.number().nullish(), + resources: z.array( + z.object({ + hash: z.string().nullish(), + name: z.string(), + type: z.string(), + weight: z.number().nullish(), + }), + ).nullish(), + ModelHash: z.string().nullish(), + HiresSteps: z.string().nullish(), + HiresUpscale: z.string().nullish(), + HiresUpscaler: z.string().nullish(), + negativePrompt: z.string(), + DenoisingStrength: z.number().nullish(), +}); + +// all over the place +// export const imageSchema = z.object({ +// url: z.string().url().nullish(), +// nsfw: z.enum(["None", "Soft", "Mature"]).nullish(), +// width: z.number().nullish(), +// height: z.number().nullish(), +// hash: z.string().nullish(), +// type: z.string().nullish(), +// metadata: imageMetadataSchema.nullish(), +// meta: imageMetaSchema.nullish(), +// }); + +export const modelVersionSchema = z.object({ + id: z.number(), + modelId: z.number(), + name: z.string(), + createdAt: z.string().nullish(), + updatedAt: z.string().nullish(), + // status: z.enum(["Published", "Unpublished"]).nullish(), + status: z.string().nullish(), + publishedAt: z.string().nullish(), + trainedWords: z.array(z.string()).nullable(), + trainingStatus: z.string().nullable(), + trainingDetails: z.string().nullable(), + baseModel: z.string().nullish(), + baseModelType: z.string().nullish(), + earlyAccessTimeFrame: z.number().nullish(), + description: z.string().nullable(), + vaeId: z.number().nullable(), + stats: z.object({ + downloadCount: z.number(), + ratingCount: z.number(), + rating: z.number(), + }).nullish(), + files: z.array(fileSchema), + images: z.array(z.any()).nullish(), + 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().nullish(), + description: z.string().nullish(), + // type: z.enum(["Checkpoint", "Lora"]), // TODO: this will be important to know + type: z.string(), + poi: z.boolean().nullish(), + nsfw: z.boolean().nullish(), + allowNoCredit: z.boolean().nullish(), + allowCommercialUse: z.string().nullish(), + allowDerivatives: z.boolean().nullish(), + allowDifferentLicense: z.boolean().nullish(), + stats: statsSchema.nullish(), + creator: creatorSchema.nullish(), + tags: z.array(z.string()).nullish(), + modelVersions: z.array(modelVersionSchema), +}); From eb04f246a455cc5fe09ae66007afdd314b751f0f Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 12:53:43 -0800 Subject: [PATCH 02/11] status --- builder/modal-builder/src/volume-builder/app.py | 7 +++++++ web/src/app/(app)/api/volume-upload/route.ts | 1 + web/src/components/CheckpointList.tsx | 4 ++-- web/src/server/curdCheckpoint.ts | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/builder/modal-builder/src/volume-builder/app.py b/builder/modal-builder/src/volume-builder/app.py index 3a7e779..3eee974 100644 --- a/builder/modal-builder/src/volume-builder/app.py +++ b/builder/modal-builder/src/volume-builder/app.py @@ -2,6 +2,7 @@ import modal from config import config import os import subprocess +from pprint import pprint stub = modal.Stub() @@ -51,6 +52,8 @@ def download_model(volume_name, download_config): status = {"status": "success"} requests.post(callback_url, json={**status, **callback_body}) + print(f"finished! sending to {callback_url}") + pprint({**status, **callback_body}) @stub.local_entrypoint() @@ -63,7 +66,11 @@ def simple_download(): except modal.exception.FunctionTimeoutError as e: status = {"status": "failed", "error_logs": f"{str(e)}", "timeout": timeout} requests.post(callback_url, json={**status, **callback_body}) + print(f"finished! sending to {callback_url}") + pprint({**status, **callback_body}) except Exception as e: status = {"status": "failed", "error_logs": str(e)} requests.post(callback_url, json={**status, **callback_body}) + print(f"finished! sending to {callback_url}") + pprint({**status, **callback_body}) diff --git a/web/src/app/(app)/api/volume-upload/route.ts b/web/src/app/(app)/api/volume-upload/route.ts index f7cb3cb..25eb2d4 100644 --- a/web/src/app/(app)/api/volume-upload/route.ts +++ b/web/src/app/(app)/api/volume-upload/route.ts @@ -19,6 +19,7 @@ export async function POST(request: Request) { if (!data || error) return error; const { checkpoint_id, error_log, status, folder_path } = data; + console.log( checkpoint_id, error_log, status, folder_path ) if (status === "success") { await db diff --git a/web/src/components/CheckpointList.tsx b/web/src/components/CheckpointList.tsx index 48a2c81..01afbf7 100644 --- a/web/src/components/CheckpointList.tsx +++ b/web/src/components/CheckpointList.tsx @@ -89,7 +89,7 @@ export const columns: ColumnDef[] = [ {checkpoint.is_public ? ( - Public + Public ) : ( Private )} @@ -112,7 +112,7 @@ export const columns: ColumnDef[] = [ }, cell: ({ row }) => { return ( - + {row.original.status} ); diff --git a/web/src/server/curdCheckpoint.ts b/web/src/server/curdCheckpoint.ts index 4019b7e..acbacc9 100644 --- a/web/src/server/curdCheckpoint.ts +++ b/web/src/server/curdCheckpoint.ts @@ -115,6 +115,7 @@ function getUrl(civitai_url: string) { export const addCivitaiCheckpoint = withServerPromise( async (data: z.infer) => { const { userId, orgId } = auth(); + console.log("START") console.log("1"); if (!data.civitai_url) return { error: "no civitai_url" }; @@ -221,7 +222,7 @@ async function uploadCheckpoint( volume_name: v.volume_name, volume_id: v.id, checkpoint_id: c.id, - callback_url: `${protocol}://${domain}/api/volume-updated`, + callback_url: `${protocol}://${domain}/api/volume-upload`, upload_type: "checkpoint" }), }, From dfd139294f8062b074d603f2a08d0f9e15245956 Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 14:44:05 -0800 Subject: [PATCH 03/11] print --- builder/modal-builder/src/template/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builder/modal-builder/src/template/app.py b/builder/modal-builder/src/template/app.py index 1650252..06f8d96 100644 --- a/builder/modal-builder/src/template/app.py +++ b/builder/modal-builder/src/template/app.py @@ -36,6 +36,7 @@ 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")) + print("about to build image") dockerfile_image = ( modal.Image.debian_slim() .apt_install("git", "wget") @@ -76,6 +77,8 @@ if not deploy_test: .run_commands("python install_deps.py") ) +print("built image") + # Time to wait between API check attempts in milliseconds COMFY_API_AVAILABLE_INTERVAL_MS = 50 # Maximum number of API check attempts @@ -306,4 +309,4 @@ def comfyui_app(): }, )() - return make_simple_proxy_app(ProxyContext(config)) \ No newline at end of file + return make_simple_proxy_app(ProxyContext(config)) From 224b006ec2f6a227a82d28ff1ad7bab4b87573e2 Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 15:15:05 -0800 Subject: [PATCH 04/11] somethingg --- builder/modal-builder/src/template/app.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/builder/modal-builder/src/template/app.py b/builder/modal-builder/src/template/app.py index 06f8d96..91d5601 100644 --- a/builder/modal-builder/src/template/app.py +++ b/builder/modal-builder/src/template/app.py @@ -1,6 +1,6 @@ from config import config import modal -from modal import Image, Mount, web_endpoint, Stub, asgi_app +from modal import Image, Mount, web_endpoint, Stub, asgi_app import json import urllib.request import urllib.parse @@ -30,13 +30,11 @@ print(config) print("deploy_test ", deploy_test) print('volumes', volumes) stub = Stub(name=config["name"]) -# print(stub.app_id) if not deploy_test: # dockerfile_image = Image.from_dockerfile(f"{current_directory}/Dockerfile", context_mount=Mount.from_local_dir(f"{current_directory}/data", remote_path="/data")) # dockerfile_image = Image.from_dockerfile(f"{current_directory}/Dockerfile", context_mount=Mount.from_local_dir(f"{current_directory}/data", remote_path="/data")) - print("about to build image") dockerfile_image = ( modal.Image.debian_slim() .apt_install("git", "wget") @@ -55,6 +53,8 @@ if not deploy_test: "cd /comfyui/custom_nodes/ComfyUI-Manager && pip install -r requirements.txt", "cd /comfyui/custom_nodes/ComfyUI-Manager && mkdir startup-scripts", ) + .run_commands(f"cat /comfyui/server.py") + .run_commands(f"ls /comfyui/app") # .run_commands( # # Install comfy deploy # "cd /comfyui/custom_nodes && git clone https://github.com/BennyKok/comfyui-deploy.git", @@ -77,8 +77,6 @@ if not deploy_test: .run_commands("python install_deps.py") ) -print("built image") - # Time to wait between API check attempts in milliseconds COMFY_API_AVAILABLE_INTERVAL_MS = 50 # Maximum number of API check attempts @@ -158,8 +156,9 @@ image = Image.debian_slim() target_image = image if deploy_test else dockerfile_image - -@stub.function(image=target_image, gpu=config["gpu"], volumes=volumes) +@stub.function(image=target_image, gpu=config["gpu"] + ,volumes=volumes +) def run(input: Input): import subprocess import time @@ -168,6 +167,7 @@ def run(input: Input): command = ["python", "main.py", "--disable-auto-launch", "--disable-metadata"] + server_process = subprocess.Popen(command, cwd="/comfyui") check_server( @@ -240,7 +240,9 @@ async def bar(request_input: RequestInput): # pass -@stub.function(image=image, volumes=volumes) +@stub.function(image=image + ,volumes=volumes +) @asgi_app() def comfyui_api(): return web_app @@ -289,8 +291,8 @@ def spawn_comfyui_in_background(): # Restrict to 1 container because we want to our ComfyUI session state # to be on a single container. concurrency_limit=1, - volumes=volumes, timeout=10 * 60, + volumes=volumes, ) @asgi_app() def comfyui_app(): From db04d02d34e081165b5ff00df84b13734b9d4a75 Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 22:15:12 -0800 Subject: [PATCH 05/11] working --- builder/modal-builder/src/main.py | 4 +- .../modal-builder/src/volume-builder/app.py | 12 ++-- .../src/volume-builder/config.py | 3 +- web/src/app/(app)/api/volume-upload/route.ts | 4 +- web/src/components/CheckpointList.tsx | 32 +++++++++ web/src/db/schema.ts | 70 +++++++++---------- web/src/server/curdCheckpoint.ts | 64 +++++++++++------ 7 files changed, 124 insertions(+), 65 deletions(-) diff --git a/builder/modal-builder/src/main.py b/builder/modal-builder/src/main.py index b907fc4..46787de 100644 --- a/builder/modal-builder/src/main.py +++ b/builder/modal-builder/src/main.py @@ -47,6 +47,7 @@ machine_id_websocket_dict = {} machine_id_status = {} fly_instance_id = os.environ.get('FLY_ALLOC_ID', 'local').split('-')[0] +civitai_api_key = os.environ.get('FLY_ALLOC_ID', 'local').split('-')[0] class FlyReplayMiddleware(BaseHTTPMiddleware): @@ -272,7 +273,8 @@ async def upload_logic(body: UploadBody): "checkpoint_id": body.checkpoint_id, "volume_id": body.volume_id, "folder_path": upload_path, - } + }, + "civitai_api_key": os.environ.get('CIVITAI_API_KEY') } with open(f"{folder_path}/config.py", "w") as f: f.write("config = " + json.dumps(config)) diff --git a/builder/modal-builder/src/volume-builder/app.py b/builder/modal-builder/src/volume-builder/app.py index 3eee974..06daf86 100644 --- a/builder/modal-builder/src/volume-builder/app.py +++ b/builder/modal-builder/src/volume-builder/app.py @@ -25,16 +25,13 @@ vol_name_to_links = config["volume_names"] vol_name_to_path = config["volume_paths"] callback_url = config["callback_url"] callback_body = config["callback_body"] +civitai_key = config["civitai_api_key"] volumes = create_volumes(vol_name_to_links, vol_name_to_path) image = ( modal.Image.debian_slim().apt_install("wget").pip_install("requests") ) -print(vol_name_to_links) -print(vol_name_to_path) -print(volumes) - # download config { "download_url": "", "folder_path": ""} timeout=5000 @stub.function(volumes=volumes, image=image, timeout=timeout, gpu=None) @@ -42,20 +39,23 @@ def download_model(volume_name, download_config): import requests download_url = download_config["download_url"] folder_path = download_config["folder_path"] + volume_base_path = vol_name_to_path[volume_name] model_store_path = os.path.join(volume_base_path, folder_path) + modified_download_url = download_url + ("&" if "?" in download_url else "?") + "token=" + civitai_key + print('downlodaing', modified_download_url) - subprocess.run(["wget", download_url, "--content-disposition", "-P", model_store_path]) + subprocess.run(["wget", modified_download_url , "--content-disposition", "-P", model_store_path]) subprocess.run(["ls", "-la", volume_base_path]) subprocess.run(["ls", "-la", model_store_path]) volumes[volume_base_path].commit() + status = {"status": "success"} requests.post(callback_url, json={**status, **callback_body}) print(f"finished! sending to {callback_url}") pprint({**status, **callback_body}) - @stub.local_entrypoint() def simple_download(): import requests diff --git a/builder/modal-builder/src/volume-builder/config.py b/builder/modal-builder/src/volume-builder/config.py index 2bf9032..3abfeac 100644 --- a/builder/modal-builder/src/volume-builder/config.py +++ b/builder/modal-builder/src/volume-builder/config.py @@ -13,5 +13,6 @@ config = { "checkpoint_id": "", "volume_id": "", "folder_path": "images", - } + }, + "civitai_api_key": "", } diff --git a/web/src/app/(app)/api/volume-upload/route.ts b/web/src/app/(app)/api/volume-upload/route.ts index 25eb2d4..70edf27 100644 --- a/web/src/app/(app)/api/volume-upload/route.ts +++ b/web/src/app/(app)/api/volume-upload/route.ts @@ -26,7 +26,8 @@ export async function POST(request: Request) { .update(checkpointTable) .set({ status: "success", - folder_path + folder_path, + updated_at: new Date(), // build_log: build_log, }) .where(eq(checkpointTable.id, checkpoint_id)); @@ -37,6 +38,7 @@ export async function POST(request: Request) { .set({ status: "failed", error_log, + updated_at: new Date(), // status: "error", // build_log: build_log, }) diff --git a/web/src/components/CheckpointList.tsx b/web/src/components/CheckpointList.tsx index 01afbf7..faf014b 100644 --- a/web/src/components/CheckpointList.tsx +++ b/web/src/components/CheckpointList.tsx @@ -116,6 +116,38 @@ export const columns: ColumnDef[] = [ {row.original.status} ); + // const oneHourAgo = new Date(new Date().getTime() - (60 * 60 * 1000)); + // const lastUpdated = new Date(row.original.updated_at); + // const canRefresh = row.original.status === "failed" && lastUpdated < oneHourAgo; + // const canRefresh = row.original.status === "failed" && lastUpdated < oneHourAgo; + // cell: ({ row }) => { + // // const oneHourAgo = new Date(new Date().getTime() - (60 * 60 * 1000)); + // // const lastUpdated = new Date(row.original.updated_at); + // // const canRefresh = row.original.status === "failed" && lastUpdated < oneHourAgo; + // const canReDownload = true; + // + // return ( + //
+ // + // {row.original.status} + // + // {canReDownload && ( + // { + // redownloadCheckpoint(row.original); + // }} + // className="h-4 w-4 cursor-pointer" // Adjust the size with h-x and w-x classes + // /> + // )} + //
+ // ); + // }, }, }, { diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts index c6255ba..da3433c 100644 --- a/web/src/db/schema.ts +++ b/web/src/db/schema.ts @@ -406,13 +406,47 @@ export const checkpointTable = dbSchema.table("checkpoints", { is_public: boolean("is_public").notNull().default(false), status: resourceUpload("status").notNull().default("started"), - upload_machine_id: text("upload_machine_id"), + upload_machine_id: text("upload_machine_id"), // TODO: review if actually needed upload_type: modelUploadType("upload_type").notNull(), error_log: text("error_log"), created_at: timestamp("created_at").defaultNow().notNull(), updated_at: timestamp("updated_at").defaultNow().notNull(), }); +export const checkpointVolumeTable = dbSchema.table("checkpoint_volume", { + id: uuid("id").primaryKey().defaultRandom().notNull(), + user_id: text("user_id").references(() => usersTable.id, { + // onDelete: "cascade", + }), + org_id: text("org_id"), + volume_name: text("volume_name").notNull(), + created_at: timestamp("created_at").defaultNow().notNull(), + updated_at: timestamp("updated_at").defaultNow().notNull(), + disabled: boolean("disabled").default(false).notNull(), +}); + +export const 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 const subscriptionPlan = pgEnum("subscription_plan", [ "basic", "pro", @@ -452,40 +486,6 @@ export const insertCivitaiCheckpointSchema = createInsertSchema( } ); -export const checkpointVolumeTable = dbSchema.table("checkpoint_volume", { - id: uuid("id").primaryKey().defaultRandom().notNull(), - user_id: text("user_id").references(() => usersTable.id, { - // onDelete: "cascade", - }), - org_id: text("org_id"), - volume_name: text("volume_name").notNull(), - created_at: timestamp("created_at").defaultNow().notNull(), - updated_at: timestamp("updated_at").defaultNow().notNull(), - disabled: boolean("disabled").default(false).notNull(), -}); - -export const 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; export type WorkflowType = InferSelectModel; export type MachineType = InferSelectModel; diff --git a/web/src/server/curdCheckpoint.ts b/web/src/server/curdCheckpoint.ts index acbacc9..7dc1046 100644 --- a/web/src/server/curdCheckpoint.ts +++ b/web/src/server/curdCheckpoint.ts @@ -15,6 +15,7 @@ import { headers } from "next/headers"; import { addCivitaiCheckpointSchema } from "./addCheckpointSchema"; import { and, eq, isNull } from "drizzle-orm"; import { CivitaiModelResponse } from "@/types/civitai"; +import { CheckpointItemList } from "@/components/CheckpointList"; export async function getCheckpoints() { const { userId, orgId } = auth(); @@ -74,12 +75,12 @@ export async function getCheckpointVolumes() { } export async function retrieveCheckpointVolumes() { - let volumes = await getCheckpointVolumes() + let volumes = await getCheckpointVolumes(); if (volumes.length === 0) { // create volume if not already created - volumes = await addCheckpointVolume() - } - return volumes + volumes = await addCheckpointVolume(); + } + return volumes; } export async function addCheckpointVolume() { @@ -97,7 +98,7 @@ export async function addCheckpointVolume() { disabled: false, // Default value }) .returning(); // Returns the inserted row -return insertedVolume; + return insertedVolume; } function getUrl(civitai_url: string) { @@ -115,30 +116,24 @@ function getUrl(civitai_url: string) { export const addCivitaiCheckpoint = withServerPromise( async (data: z.infer) => { const { userId, orgId } = auth(); - console.log("START") - console.log("1"); if (!data.civitai_url) return { error: "no civitai_url" }; - console.log("2"); if (!userId) return { error: "No user id" }; - console.log("3"); const { url, modelVersionId } = getUrl(data?.civitai_url); console.log("4", url, modelVersionId); const civitaiModelRes = await fetch(url) .then((x) => x.json()) .then((a) => { - console.log(a) + console.log(a); return CivitaiModelResponse.parse(a); }); console.log("5"); if (civitaiModelRes?.modelVersions?.length === 0) { - console.log("6"); return; // no versions to download } - console.log("7"); let selectedModelVersion; let selectedModelVersionId: string | null = modelVersionId; if (!selectedModelVersionId) { @@ -153,23 +148,16 @@ export const addCivitaiCheckpoint = withServerPromise( } selectedModelVersionId = selectedModelVersion?.id.toString(); } - console.log("8"); const checkpointVolumes = await getCheckpointVolumes(); - console.log("9"); let cVolume; if (checkpointVolumes.length === 0) { - console.log("10"); const volume = await addCheckpointVolume(); - console.log("11"); cVolume = volume[0]; } else { - console.log("12"); cVolume = checkpointVolumes[0]; } - console.log("13"); - const a = await db .insert(checkpointTable) .values({ @@ -183,18 +171,52 @@ export const addCivitaiCheckpoint = withServerPromise( civitai_download_url: selectedModelVersion.files[0].downloadUrl, civitai_model_response: civitaiModelRes, checkpoint_volume_id: cVolume.id, + updated_at: new Date(), }) .returning(); - console.log("14"); const b = a[0]; await uploadCheckpoint(data, b, cVolume); - console.log("15"); // redirect(`/checkpoints/${b.id}`); }, ); +// export const redownloadCheckpoint = withServerPromise( +// async (data: CheckpointItemList) => { +// const { userId } = auth(); +// if (!userId) return { error: "No user id" }; +// +// const checkpointVolumes = await getCheckpointVolumes(); +// let cVolume; +// if (checkpointVolumes.length === 0) { +// const volume = await addCheckpointVolume(); +// cVolume = volume[0]; +// } else { +// cVolume = checkpointVolumes[0]; +// } +// +// console.log("data"); +// console.log(data); +// +// const a = await db +// .update(checkpointTable) +// .set({ +// // status: "started", +// // updated_at: new Date(), +// }) +// .returning(); +// +// const b = a[0]; +// +// console.log("b"); +// console.log(b); +// +// await uploadCheckpoint(data, b, cVolume); +// // redirect(`/checkpoints/${b.id}`); +// }, +// ); + async function uploadCheckpoint( data: z.infer, c: CheckpointType, From 4c047aea62a8f8ccdca1b6cdc6c4dde38bfbfbfd Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 22:20:00 -0800 Subject: [PATCH 06/11] clean up checkpoint build --- .../src/template/data/insert_models.py | 101 ------------------ .../src/template/data/install_deps.py | 12 +-- .../modal-builder/src/volume-builder/app.py | 4 +- 3 files changed, 7 insertions(+), 110 deletions(-) delete mode 100644 builder/modal-builder/src/template/data/insert_models.py diff --git a/builder/modal-builder/src/template/data/insert_models.py b/builder/modal-builder/src/template/data/insert_models.py deleted file mode 100644 index a189bdd..0000000 --- a/builder/modal-builder/src/template/data/insert_models.py +++ /dev/null @@ -1,101 +0,0 @@ -""" -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 diff --git a/builder/modal-builder/src/template/data/install_deps.py b/builder/modal-builder/src/template/data/install_deps.py index 2b8f8d1..22a184e 100644 --- a/builder/modal-builder/src/template/data/install_deps.py +++ b/builder/modal-builder/src/template/data/install_deps.py @@ -45,13 +45,13 @@ for package in packages: response = requests.request("POST", f"{root_url}/customnode/install", json=package, headers=headers) print(response.text) -# with open('models.json') as f: -# models = json.load(f) +with open('models.json') as f: + models = json.load(f) -# for model in models: -# response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers) -# print(response.text) +for model in models: + response = requests.request("POST", f"{root_url}/model/install", json=model, headers=headers) + print(response.text) # Close the server server_process.terminate() -print("Finished installing dependencies.") \ No newline at end of file +print("Finished installing dependencies.") diff --git a/builder/modal-builder/src/volume-builder/app.py b/builder/modal-builder/src/volume-builder/app.py index 06daf86..9f5bd73 100644 --- a/builder/modal-builder/src/volume-builder/app.py +++ b/builder/modal-builder/src/volume-builder/app.py @@ -43,7 +43,7 @@ def download_model(volume_name, download_config): volume_base_path = vol_name_to_path[volume_name] model_store_path = os.path.join(volume_base_path, folder_path) modified_download_url = download_url + ("&" if "?" in download_url else "?") + "token=" + civitai_key - print('downlodaing', modified_download_url) + print('downloading', modified_download_url) subprocess.run(["wget", modified_download_url , "--content-disposition", "-P", model_store_path]) subprocess.run(["ls", "-la", volume_base_path]) @@ -59,8 +59,6 @@ def download_model(volume_name, download_config): @stub.local_entrypoint() def simple_download(): import requests - print(vol_name_to_links) - print([(vol_name, link) for vol_name,link in vol_name_to_links.items()]) try: list(download_model.starmap([(vol_name, link) for vol_name,link in vol_name_to_links.items()])) except modal.exception.FunctionTimeoutError as e: From 0d88907cfd0c644c6b5f40e09074983eb8bbd0d2 Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 22:22:59 -0800 Subject: [PATCH 07/11] clean up web --- web/src/components/CheckpointList.tsx | 4 ++-- web/src/server/curdCheckpoint.ts | 4 ---- web/src/types/civitai.ts | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/web/src/components/CheckpointList.tsx b/web/src/components/CheckpointList.tsx index faf014b..a0fce75 100644 --- a/web/src/components/CheckpointList.tsx +++ b/web/src/components/CheckpointList.tsx @@ -78,7 +78,6 @@ export const columns: ColumnDef[] = [ }, cell: ({ row }) => { const checkpoint = row.original; - console.log(checkpoint); return ( [] = [ {row.original.status} ); + // NOTE: retry downloads on failures // const oneHourAgo = new Date(new Date().getTime() - (60 * 60 * 1000)); // const lastUpdated = new Date(row.original.updated_at); // const canRefresh = row.original.status === "failed" && lastUpdated < oneHourAgo; @@ -189,6 +189,7 @@ export const columns: ColumnDef[] = [ ), }, + // TODO: deletion and editing for future sprint // { // id: "actions", // enableHiding: false, @@ -274,7 +275,6 @@ export function CheckpointList({ data }: { data: CheckpointItemList[] }) { fieldConfig={{ civitai_url: { fieldType: "fallback", - // fieldType: "fallback", inputProps: { required: true }, description: ( <> diff --git a/web/src/server/curdCheckpoint.ts b/web/src/server/curdCheckpoint.ts index 7dc1046..3c01532 100644 --- a/web/src/server/curdCheckpoint.ts +++ b/web/src/server/curdCheckpoint.ts @@ -8,14 +8,12 @@ import { 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"; -import { CheckpointItemList } from "@/components/CheckpointList"; export async function getCheckpoints() { const { userId, orgId } = auth(); @@ -121,14 +119,12 @@ export const addCivitaiCheckpoint = withServerPromise( if (!userId) return { error: "No user id" }; const { url, modelVersionId } = getUrl(data?.civitai_url); - console.log("4", url, modelVersionId); const civitaiModelRes = await fetch(url) .then((x) => x.json()) .then((a) => { console.log(a); return CivitaiModelResponse.parse(a); }); - console.log("5"); if (civitaiModelRes?.modelVersions?.length === 0) { return; // no versions to download diff --git a/web/src/types/civitai.ts b/web/src/types/civitai.ts index 971ba8b..5e57ed0 100644 --- a/web/src/types/civitai.ts +++ b/web/src/types/civitai.ts @@ -62,7 +62,7 @@ export const imageMetaSchema = z.object({ DenoisingStrength: z.number().nullish(), }); -// all over the place +// NOTE: this definition is all over the place // export const imageSchema = z.object({ // url: z.string().url().nullish(), // nsfw: z.enum(["None", "Soft", "Mature"]).nullish(), From d8df580339d7a35b90c2429af8a5db3716392ac4 Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 22:26:42 -0800 Subject: [PATCH 08/11] remove divergent sql --- web/drizzle/0035_known_skin.sql | 64 --------------------------------- 1 file changed, 64 deletions(-) delete mode 100644 web/drizzle/0035_known_skin.sql diff --git a/web/drizzle/0035_known_skin.sql b/web/drizzle/0035_known_skin.sql deleted file mode 100644 index c600712..0000000 --- a/web/drizzle/0035_known_skin.sql +++ /dev/null @@ -1,64 +0,0 @@ -DO $$ BEGIN - CREATE TYPE "model_upload_type" AS ENUM('civitai', 'huggingface', 'other'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - CREATE TYPE "resource_upload" AS ENUM('started', 'success', 'failed'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoints" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "user_id" text, - "org_id" text, - "description" text, - "checkpoint_volume_id" uuid NOT NULL, - "model_name" text, - "folder_path" text, - "civitai_id" text, - "civitai_version_id" text, - "civitai_url" text, - "civitai_download_url" text, - "civitai_model_response" jsonb, - "hf_url" text, - "s3_url" text, - "client_url" text, - "is_public" boolean DEFAULT false NOT NULL, - "status" "resource_upload" DEFAULT 'started' NOT NULL, - "upload_machine_id" text, - "upload_type" "model_upload_type" NOT NULL, - "error_log" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoint_volume" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "user_id" text, - "org_id" text, - "volume_name" text NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL, - "disabled" boolean DEFAULT false NOT NULL -); ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_checkpoint_volume_id_checkpoint_volume_id_fk" FOREIGN KEY ("checkpoint_volume_id") REFERENCES "comfyui_deploy"."checkpoint_volume"("id") ON DELETE cascade ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "comfyui_deploy"."checkpoint_volume" ADD CONSTRAINT "checkpoint_volume_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; From a295df973f0aaa1a18dbe51b376e738134daf44e Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 22:28:50 -0800 Subject: [PATCH 09/11] clean up --- web/src/app/(app)/api/volume-upload/route.ts | 1 - web/src/components/custom-form/checkpoint-input.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/app/(app)/api/volume-upload/route.ts b/web/src/app/(app)/api/volume-upload/route.ts index 70edf27..b21741e 100644 --- a/web/src/app/(app)/api/volume-upload/route.ts +++ b/web/src/app/(app)/api/volume-upload/route.ts @@ -32,7 +32,6 @@ export async function POST(request: Request) { }) .where(eq(checkpointTable.id, checkpoint_id)); } else { - // console.log(data); await db .update(checkpointTable) .set({ diff --git a/web/src/components/custom-form/checkpoint-input.tsx b/web/src/components/custom-form/checkpoint-input.tsx index 7c24e26..9a21cb9 100644 --- a/web/src/components/custom-form/checkpoint-input.tsx +++ b/web/src/components/custom-form/checkpoint-input.tsx @@ -1,4 +1,4 @@ -// NOTE: this is work in progress +// NOTE: this is WIP for doing client side validation for civitai model downloading import type { AutoFormInputComponentProps } from "../ui/auto-form/types"; import { FormControl, FormItem, FormLabel } from "../ui/form"; import { LoadingIcon } from "@/components/LoadingIcon"; From 982ef0780d60ed717334a6b66a5105d2f398c2cf Mon Sep 17 00:00:00 2001 From: Nicholas Koben Kao Date: Wed, 24 Jan 2024 22:32:48 -0800 Subject: [PATCH 10/11] lock --- web/bun.lockb | Bin 499775 -> 500223 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/web/bun.lockb b/web/bun.lockb index ed42cf30664004b10056a212496a5649d148de8b..97ec0c6e2f0fa4f710937eca6f5166877634008b 100755 GIT binary patch delta 14712 zcmeI24RBP|702&pA4y0tJ_%s>ju8AnNWzzVLlmTfK%*%F5kfX$0~DZC66{nq1VoO`Kwnl14`;E4>*0y8+|9vMhp_>pe?=2m3 zC+EJC-=2Nv-gD2r@9lf{xlyN$JT^0w!ns}x9DOFwM_hpoXtdn%X62#CfsdW!A1GVk}-K~tuO5a+Z z-;UB8UJt3Xx0PFXmip-C%nY-8LB!qj>Ru>#E!6bI|^B?W8B>+n9nYE zr<$jhBgHza^q@CX?!V8ga#NK0oKn^1sulQ7!3un*trF8fy^p#3Sw=%sn!tz)GJkzOnrnbtaL{(G}Kb=L##0hcRA z$Xj}!H>gy_%uiO!M0nsz*vjb9CT2L{qzfOiTH4gRE*x8{Wn$}U=|HHXHpYmRWASTL z+n8KYd)%?Zi+V28&0)n@BhrdFYf^mgv!gN;CLK&TfE_d8ev8>=peJkzqK_EI+iFP{+)%!k;ZWRFB}s` z9E)G0eKI)dSo|8X4ZlMCV;TOWYunAm_Te`J6~u=*=Ta4a7Iv-)HtDYv;qT%?Ynv7jLxuc)GZub!om_JL^&( zd9+*v5^La~0=pajMZH>8pt9zVTy%ibNTlL1^|1S@wC7x7j5aw0u<9W`x zjo!k5r)Yx;{?>X3#zo;mW<{;NeC8^k|nd_$rdw-yS;1j957qze!dYA}?h9wYcxRlzkz+#<)js z3s}@$GT5J^G)Vg$6gIJryJT!~(nUw)R?>OL)&%ayONm3Fjv57{t1;%f;|=+JWnrbJ zFLp(CQ33TNvu-7Ac2eDWqYYbo*M(#8YuFRki#ry-%)g!-&u%*wzf&$Nxy)e)uo7wEBPS}{z%23!- zwBDPZ|4CymWai>JL@1{Y5&9IBzOg*G!5dtwj=)u9zgu+!PWzPxoYwJ5H)H6$>;CHI ztB3L6_xxeR<%+EIKjc{n{I5z^WKX~q6ieOU1#kd_$HPTdra^Uzt+v&%`Di1i%#KE+ z2CL-2SFMqdjeD;*VvAo3J}_=YkGZzYc-R^uPW3`xc`5d-sHu-wUxI2En|SF?%)!Up z^J?}jPpkQYqTRvIAGY_uCP}$Qc{r5d`pM@ z=PJ`eYJf0qG@LGMpfJ48Qgek3GWYM)QjBZmBdon@fqaxIjQ6Z}3L6YY_t@vI77H6H zcKDg3mIxar46icPQene|VXKhJ6*j_R2KQ^JN9Y}8q!{*x-i$2{ENQ~9A6orP*mYoT z{Ld*J7M3n{cq6JRg^dx0_YG>Zuwq$nB6lZv%q=h#a3>6T@*9N>hQ1BLa+5H; z+*LaxM5(dDhT{KPVK;*@TEid*IT9%2#cnwCI$;@HnK*(YAcq8I3L6RbqOdGsX<#pL zxKSp6(aCj?mxWCdyHQ|ggiRK^(O}KOZV@{p9q<)_Qw5FzI}6qa$7x_3o$Db9VmD9h zZUC#I5z2gFH$smGW4mt$W4_!3`4M!MyTxuS^dD>1gC3O&$dKO*Suck7;K<&MgZPB4 zz>(2n{;!121m%$|19BI1I?Th7jpm$K3Y`h+6_y2^(PV-KG>ktDCqR;T7L?8Skm^K8 zvakoh*qBL>QhotttJqD3{swY}3CiJQvT-lUIZ3#0h}|t6j}X$Cl-&*m1>sP*|fdu6SP(b__>;_X`k4feCs->~e%1FxuJ<#hR%%?M82E)G`vJc_s*ffjb z3h3!FPOk`C3C3Ju`H8SR=**QxIQ|qzPUCwa--FIb{#@)c@TZC(VX$0||63sO0Ba#r zWNyC$#>jXfClEr0@?BxY(ElTm`JJ$JU{642gno}B+qE9T8N&$uN$h;kIXlSyOvdpq zft*BZ*!sVS;Rfhi!3uFaFU$|U3XH9PPgp7RCDQ2kg_VI#6ZThO9rhScCb4F4@i$&}8rcJ-rUT`P4@+V|uWlVZ#!8xsI!MQ#M m#wC5IGjEl;zkk#F$?>OMN%j3Lux|fD&+G9`&+7?WzV=^lV5lGf delta 9458 zcmeI1Yitx%6vt<~(`~6xw%tN`ls;gKEtL1744tgiRWF#fqrUt6#y72i!*$5NwxE`v{(idx+-(|4@I`kEF!UP?(+yz?{!;Gb800`oh!Kn0Bc!587pLcaR3@)79S-c+BNfvWjx?Dutw(o0cx2j28fM%?;_g61xlv~=2mVzjB0cmlz0NDNu+AE%3Gvd3 zbca7rsl&{8~(hV;Xy+vskYwXFU zm%08Hb*?Yp>?>Zrs?@#MrgS$KW=U;W_xkFq*B%dR(BVc$Z@eQOU_!@4>5aGjwRiE< z==x2>Y!qIXXg%Q0!{jT@hC5%qDaNVm^}rj?&Ra4#$nQ$i8|+D1YlA%pKAYWf;#nCR zD4U&r2RGYTde@j)@v!1y)qqt4Rt;D+VAX(C16B=KHDJ|%RRdNHST$hPfK>xl4gB|N zAX_^cP-COAj%X(XYF)k!zdAY~!;GIFl|=ldB`gLqg3Lu0i}5(JBr^Hoaxz&jt-3;W z`Fr!yL%=h5DVYqvT`GBGeZZoi_kg@E#%TR$hacaS);Jw0{mFV__$^rqm>pv$#7CB9 z*!h(Nz;eQLPEG{dL6$-00`rp%BufIT0F%WBk>MW|WgkSEo2)nV3Y{oQCRsAZhZMg- z2LsCK`ao(Va%fpJ>-;j+a%K(cZ8$&h_Y+E!w&^%;=pgX|C$FX3NO1~Q)Utl0@Dh)HC ze+gaEJPo~scQ9lX?Pj2tlROMrO*Rv~lwuZS0d%RIS!CJJ>6?`1vLjuaw+xyBWt0AcEiE0fJu>+lF2=B9!zR%1=&dGXE=AIiu{XX zqacSNQWSDZVWS}fXeg%?HU^SPwua0DmPWRgY%JIU{!Ors>=CeqWb4TumHLZk(QF{h z1^bEle1S~v>21WWVK{-!8BPJ?KoK>^x0%5$mW4HAsiC5lVs0A-^^&80@G75AJ7LVp*#)f zKwktI%UiLDlNW$BA%qgjvt$dQ|HjBPlRXC}PDq5#p%=dvK`Nn3gx;s!^KyO(iNFsC z7ejv!&WQICHVNwz$SyGP{v)!b(6@n!^BE< zl#S(-7syJXi`R0Zi)1UHi_^kBBU=ewQYNRoM7D}d)@UJH4c#XvLc1(Xp20;@QD|lL z+N4Hxpmrmms==8@)$ZN2q7&+?w$$KvC)BeJt)fm%*IMe-60Puzx=8cXt4Fl9dUawj zr9tgui>s>VGS^MZENxU Date: Wed, 24 Jan 2024 22:37:02 -0800 Subject: [PATCH 11/11] generate --- web/drizzle/0042_windy_madelyne_pryor.sql | 64 ++ web/drizzle/meta/0042_snapshot.json | 1273 +++++++++++++++++++++ web/drizzle/meta/_journal.json | 7 + 3 files changed, 1344 insertions(+) create mode 100644 web/drizzle/0042_windy_madelyne_pryor.sql create mode 100644 web/drizzle/meta/0042_snapshot.json diff --git a/web/drizzle/0042_windy_madelyne_pryor.sql b/web/drizzle/0042_windy_madelyne_pryor.sql new file mode 100644 index 0000000..c600712 --- /dev/null +++ b/web/drizzle/0042_windy_madelyne_pryor.sql @@ -0,0 +1,64 @@ +DO $$ BEGIN + CREATE TYPE "model_upload_type" AS ENUM('civitai', 'huggingface', 'other'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + CREATE TYPE "resource_upload" AS ENUM('started', 'success', 'failed'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoints" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" text, + "org_id" text, + "description" text, + "checkpoint_volume_id" uuid NOT NULL, + "model_name" text, + "folder_path" text, + "civitai_id" text, + "civitai_version_id" text, + "civitai_url" text, + "civitai_download_url" text, + "civitai_model_response" jsonb, + "hf_url" text, + "s3_url" text, + "client_url" text, + "is_public" boolean DEFAULT false NOT NULL, + "status" "resource_upload" DEFAULT 'started' NOT NULL, + "upload_machine_id" text, + "upload_type" "model_upload_type" NOT NULL, + "error_log" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "comfyui_deploy"."checkpoint_volume" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" text, + "org_id" text, + "volume_name" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "disabled" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "comfyui_deploy"."checkpoints" ADD CONSTRAINT "checkpoints_checkpoint_volume_id_checkpoint_volume_id_fk" FOREIGN KEY ("checkpoint_volume_id") REFERENCES "comfyui_deploy"."checkpoint_volume"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "comfyui_deploy"."checkpoint_volume" ADD CONSTRAINT "checkpoint_volume_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfyui_deploy"."users"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/web/drizzle/meta/0042_snapshot.json b/web/drizzle/meta/0042_snapshot.json new file mode 100644 index 0000000..f517368 --- /dev/null +++ b/web/drizzle/meta/0042_snapshot.json @@ -0,0 +1,1273 @@ +{ + "id": "4bbd69a1-bb1f-467b-a6d8-8412142c4c32", + "prevId": "ca1ab9de-49df-4d4a-81ae-d3db96bf55ad", + "version": "5", + "dialect": "pg", + "tables": { + "api_keys": { + "name": "api_keys", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + } + }, + "auth_requests": { + "name": "auth_requests", + "schema": "comfyui_deploy", + "columns": { + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_hash": { + "name": "api_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expired_date": { + "name": "expired_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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 + }, + "folder_path": { + "name": "folder_path", + "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 + }, + "error_log": { + "name": "error_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_checkpoint_volume_id_fk": { + "name": "checkpoints_checkpoint_volume_id_checkpoint_volume_id_fk", + "tableFrom": "checkpoints", + "tableTo": "checkpoint_volume", + "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": { + "name": "deployments", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_version_id": { + "name": "workflow_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "machine_id": { + "name": "machine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "share_slug": { + "name": "share_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "showcase_media": { + "name": "showcase_media", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "environment": { + "name": "environment", + "type": "deployment_environment", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "deployments_user_id_users_id_fk": { + "name": "deployments_user_id_users_id_fk", + "tableFrom": "deployments", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_workflow_version_id_workflow_versions_id_fk": { + "name": "deployments_workflow_version_id_workflow_versions_id_fk", + "tableFrom": "deployments", + "tableTo": "workflow_versions", + "columnsFrom": [ + "workflow_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployments_workflow_id_workflows_id_fk": { + "name": "deployments_workflow_id_workflows_id_fk", + "tableFrom": "deployments", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_machine_id_machines_id_fk": { + "name": "deployments_machine_id_machines_id_fk", + "tableFrom": "deployments", + "tableTo": "machines", + "columnsFrom": [ + "machine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "deployments_share_slug_unique": { + "name": "deployments_share_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "share_slug" + ] + } + } + }, + "machines": { + "name": "machines", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auth_token": { + "name": "auth_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "machine_type", + "primaryKey": false, + "notNull": true, + "default": "'classic'" + }, + "status": { + "name": "status", + "type": "machine_status", + "primaryKey": false, + "notNull": true, + "default": "'ready'" + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "models": { + "name": "models", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "gpu": { + "name": "gpu", + "type": "machine_gpu", + "primaryKey": false, + "notNull": false + }, + "build_machine_instance_id": { + "name": "build_machine_instance_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "build_log": { + "name": "build_log", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "machines_user_id_users_id_fk": { + "name": "machines_user_id_users_id_fk", + "tableFrom": "machines", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "subscription_status": { + "name": "subscription_status", + "schema": "comfyui_deploy", + "columns": { + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "subscription_plan", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "subscription_plan_status", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_item_plan_id": { + "name": "subscription_item_plan_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_item_api_id": { + "name": "subscription_item_api_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user_usage": { + "name": "user_usage", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usage_time": { + "name": "usage_time", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_usage_user_id_users_id_fk": { + "name": "user_usage_user_id_users_id_fk", + "tableFrom": "user_usage", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_run_outputs": { + "name": "workflow_run_outputs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_run_outputs_run_id_workflow_runs_id_fk": { + "name": "workflow_run_outputs_run_id_workflow_runs_id_fk", + "tableFrom": "workflow_run_outputs", + "tableTo": "workflow_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_runs": { + "name": "workflow_runs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workflow_version_id": { + "name": "workflow_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workflow_inputs": { + "name": "workflow_inputs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "machine_id": { + "name": "machine_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "workflow_run_origin", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "status": { + "name": "status", + "type": "workflow_run_status", + "primaryKey": false, + "notNull": true, + "default": "'not-started'" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "gpu": { + "name": "gpu", + "type": "machine_gpu", + "primaryKey": false, + "notNull": false + }, + "machine_type": { + "name": "machine_type", + "type": "machine_type", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "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" + } + }, + "model_upload_type": { + "name": "model_upload_type", + "values": { + "civitai": "civitai", + "huggingface": "huggingface", + "other": "other" + } + }, + "resource_upload": { + "name": "resource_upload", + "values": { + "started": "started", + "success": "success", + "failed": "failed" + } + }, + "subscription_plan": { + "name": "subscription_plan", + "values": { + "basic": "basic", + "pro": "pro", + "enterprise": "enterprise" + } + }, + "subscription_plan_status": { + "name": "subscription_plan_status", + "values": { + "active": "active", + "deleted": "deleted", + "paused": "paused" + } + }, + "workflow_run_origin": { + "name": "workflow_run_origin", + "values": { + "manual": "manual", + "api": "api", + "public-share": "public-share" + } + }, + "workflow_run_status": { + "name": "workflow_run_status", + "values": { + "not-started": "not-started", + "running": "running", + "uploading": "uploading", + "success": "success", + "failed": "failed" + } + } + }, + "schemas": { + "comfyui_deploy": "comfyui_deploy" + }, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/web/drizzle/meta/_journal.json b/web/drizzle/meta/_journal.json index 60056be..19546f0 100644 --- a/web/drizzle/meta/_journal.json +++ b/web/drizzle/meta/_journal.json @@ -295,6 +295,13 @@ "when": 1706111421524, "tag": "0041_thick_norrin_radd", "breakpoints": true + }, + { + "idx": 42, + "version": "5", + "when": 1706164614659, + "tag": "0042_windy_madelyne_pryor", + "breakpoints": true } ] } \ No newline at end of file