Skip to content

Commit

Permalink
backend refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
diStyApps committed Dec 15, 2024
1 parent aba029a commit 20442f6
Show file tree
Hide file tree
Showing 15 changed files with 956 additions and 859 deletions.
848 changes: 3 additions & 845 deletions __init__.py

Large diffs are not rendered by default.

496 changes: 496 additions & 0 deletions flow/api_handlers.py

Large diffs are not rendered by default.

64 changes: 64 additions & 0 deletions flow/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import logging
import mimetypes
from pathlib import Path
import re
APP_NAME = "Flow"
APP_VERSION = "0.4.6"
FLOWMSG = f"\033[38;5;129mFlow - {APP_VERSION}\033[0m"
APP_CONFIGS = []

CURRENT_DIR = Path(__file__).parent
ROOT_DIR = CURRENT_DIR.parent
WEBROOT = ROOT_DIR / "web"
CORE_PATH = WEBROOT / "core"
FLOW_PATH = WEBROOT / "flow"
FLOWS_PATH = WEBROOT / "flows"
LINKER_PATH = WEBROOT / "linker"
CUSTOM_THEMES_DIR = WEBROOT / 'custom-themes'
WEB_DIRECTORY = "web/core/js/common/scripts"

CUSTOM_NODES_DIR = ROOT_DIR.parent
EXTENSION_NODE_MAP_PATH = ROOT_DIR.parent / "ComfyUI-Manager" / "extension-node-map.json"

FLOWS_DOWNLOAD_PATH = 'https://github.com/diStyApps/flows_lib'

SAFE_FOLDER_NAME_REGEX = re.compile(r'^[\w\-]+$')
ALLOWED_EXTENSIONS = {'css'}
mimetypes.add_type('application/javascript', '.js')
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
FLOWS_CONFIG_FILE = 'flowConfig.json'
FLOWS_TO_REMOVE = [
"afl_CogVideoX-Fun-i2v-es",
"afl_CogVideoX-Fun-i2v",
"afl_MimicMotioni2v",
"afl_abase",
"afl_abasei2i",
"afl_abasesd35t3v",
"afl_abasevea",
"afl_abaseveai2i",
"afl_base-fluxd_at2i",
"afl_base-fluxdggufi2i",
"afl_base-fluxdgguft2i",
"afl_base-fluxdi2i",
"afl_base-fluxs_ai2t",
"afl_base-fluxsi2i",
"afl_baseAD",
"afl_baseAdLcm",
"afl_cogvidx_at2v",
"afl_cogvidxi2v",
"afl_cogvidxinteri2v",
"afl_flowup",
"afl_flux_dev",
"afl_flux_dev_lora",
"afl_genfill",
"afl_ipivsMorph",
"afl_mochi2v",
"afl_pulid_flux",
"afl_pulid_flux_GGUF",
"afl_reactor"
"5otvy-cogvideox-orbit-left-lora",
]

NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
66 changes: 66 additions & 0 deletions flow/downloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import subprocess
import tempfile
import shutil
from pathlib import Path
from .constants import FLOWS_DOWNLOAD_PATH, FLOWS_PATH, FLOWS_TO_REMOVE, FLOWMSG, logger

def download_update_flows() -> None:
try:
for flow in FLOWS_TO_REMOVE:
flow_path = FLOWS_PATH / flow
if flow_path.exists() and flow_path.is_dir():
# logger.info(f"{FLOWMSG}: Removing existing flow directory '{flow}'")
shutil.rmtree(flow_path)
# logger.debug(f"{FLOWMSG}: Successfully removed '{flow}'")

with tempfile.TemporaryDirectory() as tmpdirname:
temp_repo_path = Path(tmpdirname) / "Flows"
logger.info(f"{FLOWMSG}: Downloading and Upading Flows")

result = subprocess.run(
['git', 'clone', FLOWS_DOWNLOAD_PATH, str(temp_repo_path)],
capture_output=True,
text=True
)
if result.returncode != 0:
logger.error(f"{FLOWMSG}: Failed to clone flows repository:\n{result.stderr}")
return
else:
# logger.debug(f"{FLOWMSG}: Successfully cloned flows repository")
pass

if not FLOWS_PATH.exists():
FLOWS_PATH.mkdir(parents=True)
# logger.debug(f"{FLOWMSG}: Created flows directory at '{FLOWS_PATH}'")

for item in temp_repo_path.iterdir():
if item.name in ['.git', '.github']:
# logger.debug(f"{FLOWMSG}: Skipping directory '{item.name}'")
continue
dest_item = FLOWS_PATH / item.name
if item.is_dir():
if dest_item.exists():
# logger.info(f"{FLOWMSG}: Updating existing directory '{item.name}'")
_copy_directory(item, dest_item)
else:
shutil.copytree(item, dest_item)
# logger.info(f"{FLOWMSG}: Copied new directory '{item.name}'")
else:
shutil.copy2(item, dest_item)
# logger.info(f"{FLOWMSG}: Copied file '{item.name}'")

logger.info(f"{FLOWMSG}: Flows have been updated successfully.")
except Exception as e:
logger.error(f"{FLOWMSG}: An error occurred while downloading or updating flows: {e}")

def _copy_directory(src: Path, dest: Path) -> None:
for item in src.iterdir():
if item.name in ['.git', '.github']:
continue
dest_item = dest / item.name
if item.is_dir():
if not dest_item.exists():
dest_item.mkdir()
_copy_directory(item, dest_item)
else:
shutil.copy2(item, dest_item)
98 changes: 98 additions & 0 deletions flow/flow_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import json
from pathlib import Path
from aiohttp import web
from typing import Dict, Any
from .constants import (
FLOWS_PATH, CORE_PATH, LINKER_PATH, FLOW_PATH, APP_CONFIGS, FLOWMSG,FLOWS_CONFIG_FILE, logger
)
from .route_manager import RouteManager
from .api_handlers import (
list_themes_handler, get_theme_css_handler, flow_version_handler,
apps_handler, extension_node_map_handler,
install_package_handler, update_package_handler, uninstall_package_handler,
installed_custom_nodes_handler, preview_flow_handler,
reset_preview_handler, create_flow_handler, update_flow_handler, delete_flow_handler
)

class FlowManager:
@staticmethod
def setup_app_routes(app: web.Application) -> None:
try:
FlowManager._setup_flows_routes(app)

FlowManager._setup_core_routes(app)

FlowManager._setup_api_routes(app)

FlowManager._setup_additional_routes(app)

except Exception as e:
logger.error(f"{FLOWMSG}: Failed to set up routes: {e}")

@staticmethod
def _setup_flows_routes(app: web.Application) -> None:
for flow_dir in filter(lambda d: d.is_dir(), FLOWS_PATH.iterdir()):
conf_file = flow_dir / FLOWS_CONFIG_FILE
if not conf_file.is_file():
# logger.warning(f"{FLOWMSG}: Config file not found in {flow_dir}")
continue

conf = FlowManager._load_config(conf_file)
flow_url = conf.get('url')
if not flow_url:
logger.warning(f"{FLOWMSG}: Missing 'url' in config for {flow_dir}")
continue

app.add_routes(RouteManager.create_routes(f"flow/{flow_url}", flow_dir))
APP_CONFIGS.append(conf)

@staticmethod
def _setup_core_routes(app: web.Application) -> None:
if CORE_PATH.is_dir():
app.router.add_get('/core/css/themes/list', list_themes_handler)
app.router.add_get('/core/css/themes/{filename}', get_theme_css_handler)
app.router.add_static('/core/', path=CORE_PATH, name='core')

@staticmethod
def _setup_api_routes(app: web.Application) -> None:
api_routes = [
(f'/flow/api/apps', 'GET', apps_handler),
(f'/flow/api/extension-node-map', 'GET', extension_node_map_handler),
(f'/flow/api/install-package', 'POST', install_package_handler),
(f'/flow/api/update-package', 'POST', update_package_handler),
(f'/flow/api/uninstall-package', 'POST', uninstall_package_handler),
(f'/flow/api/flow-version', 'GET', flow_version_handler),
(f'/flow/api/installed-custom-nodes', 'GET', installed_custom_nodes_handler),
(f'/flow/api/preview-flow', 'POST', preview_flow_handler),
(f'/flow/api/reset-preview', 'POST', reset_preview_handler),
(f'/flow/api/create-flow', 'POST', create_flow_handler),
(f'/flow/api/update-flow', 'POST', update_flow_handler),
(f'/flow/api/delete-flow', 'DELETE', delete_flow_handler),
]

for path, method, handler in api_routes:
if method == 'GET':
app.router.add_get(path, handler)
elif method == 'POST':
app.router.add_post(path, handler)
elif method == 'DELETE':
app.router.add_delete(path, handler)

@staticmethod
def _setup_additional_routes(app: web.Application) -> None:
if LINKER_PATH.is_dir():
app.add_routes(RouteManager.create_routes('flow/linker', LINKER_PATH))
if FLOW_PATH.is_dir():
app.add_routes(RouteManager.create_routes('flow', FLOW_PATH))

@staticmethod
def _load_config(conf_file: Path) -> Dict[str, Any]:
try:
with conf_file.open('r') as f:
return json.load(f)
except json.JSONDecodeError as e:
logger.error(f"{FLOWMSG}: Invalid JSON in {conf_file}: {e}")
return {}
except Exception as e:
logger.error(f"{FLOWMSG}: Error loading config from {conf_file}: {e}")
return {}
24 changes: 24 additions & 0 deletions flow/flow_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Flow:
def __init__(self):
pass

@classmethod
def INPUT_TYPES(s):
return {
"required": {
},
}

RETURN_TYPES = ("Flow",)
FUNCTION = "flow"
CATEGORY = '🅓 diSty/Flow'
def flow(self):
return "Flow"

NODE_CLASS_MAPPINGS = {
"Flow": Flow
}

NODE_DISPLAY_NAME_MAPPINGS = {
"Flow": "Flow"
}
23 changes: 23 additions & 0 deletions flow/route_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# flow/route_manager.py

from aiohttp import web
from pathlib import Path

class RouteManager:

@staticmethod
def create_routes(base_path: str, app_dir: Path) -> web.RouteTableDef:
routes = web.RouteTableDef()
index_html = app_dir / 'index.html'

@routes.get(f"/{base_path}")
async def serve_html(request: web.Request) -> web.FileResponse:
return web.FileResponse(index_html)

for static_dir in ['css', 'js', 'media']:
static_path = app_dir / static_dir
if static_path.is_dir():
routes.static(f"/{static_dir}/", path=static_path)

routes.static(f"/{base_path}/", path=app_dir, show_index=False)
return routes
19 changes: 19 additions & 0 deletions flow/server_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from aiohttp import web
import server
from .flow_manager import FlowManager
from .downloader import download_update_flows
from .constants import FLOWMSG, logger

def setup_server() -> None:
try:
server_instance = server.PromptServer.instance
except Exception as e:
logger.error(f"{FLOWMSG}: Failed to get server instance: {e}")
return

download_update_flows()

try:
FlowManager.setup_app_routes(server_instance.app)
except Exception as e:
logger.error(f"{FLOWMSG}: Failed to set up app routes: {e}")
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "comfyui-disty-flow"
description = "Flow is a custom node designed to provide a more user-friendly interface for ComfyUI by acting as an alternative user interface for running workflows. It is not a replacement for workflow creation.\nFlow is currently in the early stages of development, so expect bugs and ongoing feature enhancements. With your support and feedback, Flow will settle into a steady stream."
version = "0.4.5"
version = "0.4.6"
license = {file = "LICENSE"}

[project.urls]
Expand Down
2 changes: 1 addition & 1 deletion web/core/js/common/components/footer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(function() {
async function getVersion() {
try {
const response = await fetch('/api/flow-version');
const response = await fetch('/flow/api/flow-version');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
Expand Down
8 changes: 4 additions & 4 deletions web/core/js/common/components/missingPackagesDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function enrichMissingCustomPackages(missingCustomPackages, flowConfig) {

async function fetchInstalledCustomNodes() {
try {
const response = await fetch('/api/installed-custom-nodes');
const response = await fetch('/flow/api/installed-custom-nodes');
if (response.ok) {
const data = await response.json();
return data.installedNodes || [];
Expand Down Expand Up @@ -311,7 +311,7 @@ function handleButtonClick(button, requestFunction) {

function sendInstallRequest(packageUrl, downloadModal, statusMessageElement) {
statusMessageElement.textContent = 'Installing...';
return fetch('/api/install-package', {
return fetch('/flow/api/install-package', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ packageUrl, downloadModal })
Expand All @@ -334,7 +334,7 @@ function sendInstallRequest(packageUrl, downloadModal, statusMessageElement) {

function sendUpdateRequest(packageUrl, downloadModal, statusMessageElement) {
statusMessageElement.textContent = 'Updating...';
return fetch('/api/update-package', {
return fetch('/flow/api/update-package', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ packageUrl })
Expand All @@ -357,7 +357,7 @@ function sendUpdateRequest(packageUrl, downloadModal, statusMessageElement) {

function sendUninstallRequest(packageUrl, downloadModal, statusMessageElement) {
statusMessageElement.textContent = 'Uninstalling...';
return fetch('/api/uninstall-package', {
return fetch('/flow/api/uninstall-package', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ packageUrl })
Expand Down
2 changes: 1 addition & 1 deletion web/core/js/common/scripts/nodesscanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async function fetchNodeInfo(classType) {

async function fetchExtensionNodeMap() {
try {
const response = await fetch('/api/extension-node-map');
const response = await fetch('/flow/api/extension-node-map');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
Expand Down
Loading

0 comments on commit 20442f6

Please sign in to comment.