Skip to content

Commit 20442f6

Browse files
committed
backend refactoring
1 parent aba029a commit 20442f6

File tree

15 files changed

+956
-859
lines changed

15 files changed

+956
-859
lines changed

__init__.py

Lines changed: 3 additions & 845 deletions
Large diffs are not rendered by default.

flow/api_handlers.py

Lines changed: 496 additions & 0 deletions
Large diffs are not rendered by default.

flow/constants.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import logging
2+
import mimetypes
3+
from pathlib import Path
4+
import re
5+
APP_NAME = "Flow"
6+
APP_VERSION = "0.4.6"
7+
FLOWMSG = f"\033[38;5;129mFlow - {APP_VERSION}\033[0m"
8+
APP_CONFIGS = []
9+
10+
CURRENT_DIR = Path(__file__).parent
11+
ROOT_DIR = CURRENT_DIR.parent
12+
WEBROOT = ROOT_DIR / "web"
13+
CORE_PATH = WEBROOT / "core"
14+
FLOW_PATH = WEBROOT / "flow"
15+
FLOWS_PATH = WEBROOT / "flows"
16+
LINKER_PATH = WEBROOT / "linker"
17+
CUSTOM_THEMES_DIR = WEBROOT / 'custom-themes'
18+
WEB_DIRECTORY = "web/core/js/common/scripts"
19+
20+
CUSTOM_NODES_DIR = ROOT_DIR.parent
21+
EXTENSION_NODE_MAP_PATH = ROOT_DIR.parent / "ComfyUI-Manager" / "extension-node-map.json"
22+
23+
FLOWS_DOWNLOAD_PATH = 'https://github.com/diStyApps/flows_lib'
24+
25+
SAFE_FOLDER_NAME_REGEX = re.compile(r'^[\w\-]+$')
26+
ALLOWED_EXTENSIONS = {'css'}
27+
mimetypes.add_type('application/javascript', '.js')
28+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
29+
logger = logging.getLogger(__name__)
30+
FLOWS_CONFIG_FILE = 'flowConfig.json'
31+
FLOWS_TO_REMOVE = [
32+
"afl_CogVideoX-Fun-i2v-es",
33+
"afl_CogVideoX-Fun-i2v",
34+
"afl_MimicMotioni2v",
35+
"afl_abase",
36+
"afl_abasei2i",
37+
"afl_abasesd35t3v",
38+
"afl_abasevea",
39+
"afl_abaseveai2i",
40+
"afl_base-fluxd_at2i",
41+
"afl_base-fluxdggufi2i",
42+
"afl_base-fluxdgguft2i",
43+
"afl_base-fluxdi2i",
44+
"afl_base-fluxs_ai2t",
45+
"afl_base-fluxsi2i",
46+
"afl_baseAD",
47+
"afl_baseAdLcm",
48+
"afl_cogvidx_at2v",
49+
"afl_cogvidxi2v",
50+
"afl_cogvidxinteri2v",
51+
"afl_flowup",
52+
"afl_flux_dev",
53+
"afl_flux_dev_lora",
54+
"afl_genfill",
55+
"afl_ipivsMorph",
56+
"afl_mochi2v",
57+
"afl_pulid_flux",
58+
"afl_pulid_flux_GGUF",
59+
"afl_reactor"
60+
"5otvy-cogvideox-orbit-left-lora",
61+
]
62+
63+
NODE_CLASS_MAPPINGS = {}
64+
NODE_DISPLAY_NAME_MAPPINGS = {}

flow/downloader.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import subprocess
2+
import tempfile
3+
import shutil
4+
from pathlib import Path
5+
from .constants import FLOWS_DOWNLOAD_PATH, FLOWS_PATH, FLOWS_TO_REMOVE, FLOWMSG, logger
6+
7+
def download_update_flows() -> None:
8+
try:
9+
for flow in FLOWS_TO_REMOVE:
10+
flow_path = FLOWS_PATH / flow
11+
if flow_path.exists() and flow_path.is_dir():
12+
# logger.info(f"{FLOWMSG}: Removing existing flow directory '{flow}'")
13+
shutil.rmtree(flow_path)
14+
# logger.debug(f"{FLOWMSG}: Successfully removed '{flow}'")
15+
16+
with tempfile.TemporaryDirectory() as tmpdirname:
17+
temp_repo_path = Path(tmpdirname) / "Flows"
18+
logger.info(f"{FLOWMSG}: Downloading and Upading Flows")
19+
20+
result = subprocess.run(
21+
['git', 'clone', FLOWS_DOWNLOAD_PATH, str(temp_repo_path)],
22+
capture_output=True,
23+
text=True
24+
)
25+
if result.returncode != 0:
26+
logger.error(f"{FLOWMSG}: Failed to clone flows repository:\n{result.stderr}")
27+
return
28+
else:
29+
# logger.debug(f"{FLOWMSG}: Successfully cloned flows repository")
30+
pass
31+
32+
if not FLOWS_PATH.exists():
33+
FLOWS_PATH.mkdir(parents=True)
34+
# logger.debug(f"{FLOWMSG}: Created flows directory at '{FLOWS_PATH}'")
35+
36+
for item in temp_repo_path.iterdir():
37+
if item.name in ['.git', '.github']:
38+
# logger.debug(f"{FLOWMSG}: Skipping directory '{item.name}'")
39+
continue
40+
dest_item = FLOWS_PATH / item.name
41+
if item.is_dir():
42+
if dest_item.exists():
43+
# logger.info(f"{FLOWMSG}: Updating existing directory '{item.name}'")
44+
_copy_directory(item, dest_item)
45+
else:
46+
shutil.copytree(item, dest_item)
47+
# logger.info(f"{FLOWMSG}: Copied new directory '{item.name}'")
48+
else:
49+
shutil.copy2(item, dest_item)
50+
# logger.info(f"{FLOWMSG}: Copied file '{item.name}'")
51+
52+
logger.info(f"{FLOWMSG}: Flows have been updated successfully.")
53+
except Exception as e:
54+
logger.error(f"{FLOWMSG}: An error occurred while downloading or updating flows: {e}")
55+
56+
def _copy_directory(src: Path, dest: Path) -> None:
57+
for item in src.iterdir():
58+
if item.name in ['.git', '.github']:
59+
continue
60+
dest_item = dest / item.name
61+
if item.is_dir():
62+
if not dest_item.exists():
63+
dest_item.mkdir()
64+
_copy_directory(item, dest_item)
65+
else:
66+
shutil.copy2(item, dest_item)

flow/flow_manager.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import json
2+
from pathlib import Path
3+
from aiohttp import web
4+
from typing import Dict, Any
5+
from .constants import (
6+
FLOWS_PATH, CORE_PATH, LINKER_PATH, FLOW_PATH, APP_CONFIGS, FLOWMSG,FLOWS_CONFIG_FILE, logger
7+
)
8+
from .route_manager import RouteManager
9+
from .api_handlers import (
10+
list_themes_handler, get_theme_css_handler, flow_version_handler,
11+
apps_handler, extension_node_map_handler,
12+
install_package_handler, update_package_handler, uninstall_package_handler,
13+
installed_custom_nodes_handler, preview_flow_handler,
14+
reset_preview_handler, create_flow_handler, update_flow_handler, delete_flow_handler
15+
)
16+
17+
class FlowManager:
18+
@staticmethod
19+
def setup_app_routes(app: web.Application) -> None:
20+
try:
21+
FlowManager._setup_flows_routes(app)
22+
23+
FlowManager._setup_core_routes(app)
24+
25+
FlowManager._setup_api_routes(app)
26+
27+
FlowManager._setup_additional_routes(app)
28+
29+
except Exception as e:
30+
logger.error(f"{FLOWMSG}: Failed to set up routes: {e}")
31+
32+
@staticmethod
33+
def _setup_flows_routes(app: web.Application) -> None:
34+
for flow_dir in filter(lambda d: d.is_dir(), FLOWS_PATH.iterdir()):
35+
conf_file = flow_dir / FLOWS_CONFIG_FILE
36+
if not conf_file.is_file():
37+
# logger.warning(f"{FLOWMSG}: Config file not found in {flow_dir}")
38+
continue
39+
40+
conf = FlowManager._load_config(conf_file)
41+
flow_url = conf.get('url')
42+
if not flow_url:
43+
logger.warning(f"{FLOWMSG}: Missing 'url' in config for {flow_dir}")
44+
continue
45+
46+
app.add_routes(RouteManager.create_routes(f"flow/{flow_url}", flow_dir))
47+
APP_CONFIGS.append(conf)
48+
49+
@staticmethod
50+
def _setup_core_routes(app: web.Application) -> None:
51+
if CORE_PATH.is_dir():
52+
app.router.add_get('/core/css/themes/list', list_themes_handler)
53+
app.router.add_get('/core/css/themes/{filename}', get_theme_css_handler)
54+
app.router.add_static('/core/', path=CORE_PATH, name='core')
55+
56+
@staticmethod
57+
def _setup_api_routes(app: web.Application) -> None:
58+
api_routes = [
59+
(f'/flow/api/apps', 'GET', apps_handler),
60+
(f'/flow/api/extension-node-map', 'GET', extension_node_map_handler),
61+
(f'/flow/api/install-package', 'POST', install_package_handler),
62+
(f'/flow/api/update-package', 'POST', update_package_handler),
63+
(f'/flow/api/uninstall-package', 'POST', uninstall_package_handler),
64+
(f'/flow/api/flow-version', 'GET', flow_version_handler),
65+
(f'/flow/api/installed-custom-nodes', 'GET', installed_custom_nodes_handler),
66+
(f'/flow/api/preview-flow', 'POST', preview_flow_handler),
67+
(f'/flow/api/reset-preview', 'POST', reset_preview_handler),
68+
(f'/flow/api/create-flow', 'POST', create_flow_handler),
69+
(f'/flow/api/update-flow', 'POST', update_flow_handler),
70+
(f'/flow/api/delete-flow', 'DELETE', delete_flow_handler),
71+
]
72+
73+
for path, method, handler in api_routes:
74+
if method == 'GET':
75+
app.router.add_get(path, handler)
76+
elif method == 'POST':
77+
app.router.add_post(path, handler)
78+
elif method == 'DELETE':
79+
app.router.add_delete(path, handler)
80+
81+
@staticmethod
82+
def _setup_additional_routes(app: web.Application) -> None:
83+
if LINKER_PATH.is_dir():
84+
app.add_routes(RouteManager.create_routes('flow/linker', LINKER_PATH))
85+
if FLOW_PATH.is_dir():
86+
app.add_routes(RouteManager.create_routes('flow', FLOW_PATH))
87+
88+
@staticmethod
89+
def _load_config(conf_file: Path) -> Dict[str, Any]:
90+
try:
91+
with conf_file.open('r') as f:
92+
return json.load(f)
93+
except json.JSONDecodeError as e:
94+
logger.error(f"{FLOWMSG}: Invalid JSON in {conf_file}: {e}")
95+
return {}
96+
except Exception as e:
97+
logger.error(f"{FLOWMSG}: Error loading config from {conf_file}: {e}")
98+
return {}

flow/flow_node.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class Flow:
2+
def __init__(self):
3+
pass
4+
5+
@classmethod
6+
def INPUT_TYPES(s):
7+
return {
8+
"required": {
9+
},
10+
}
11+
12+
RETURN_TYPES = ("Flow",)
13+
FUNCTION = "flow"
14+
CATEGORY = '🅓 diSty/Flow'
15+
def flow(self):
16+
return "Flow"
17+
18+
NODE_CLASS_MAPPINGS = {
19+
"Flow": Flow
20+
}
21+
22+
NODE_DISPLAY_NAME_MAPPINGS = {
23+
"Flow": "Flow"
24+
}

flow/route_manager.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# flow/route_manager.py
2+
3+
from aiohttp import web
4+
from pathlib import Path
5+
6+
class RouteManager:
7+
8+
@staticmethod
9+
def create_routes(base_path: str, app_dir: Path) -> web.RouteTableDef:
10+
routes = web.RouteTableDef()
11+
index_html = app_dir / 'index.html'
12+
13+
@routes.get(f"/{base_path}")
14+
async def serve_html(request: web.Request) -> web.FileResponse:
15+
return web.FileResponse(index_html)
16+
17+
for static_dir in ['css', 'js', 'media']:
18+
static_path = app_dir / static_dir
19+
if static_path.is_dir():
20+
routes.static(f"/{static_dir}/", path=static_path)
21+
22+
routes.static(f"/{base_path}/", path=app_dir, show_index=False)
23+
return routes

flow/server_setup.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from aiohttp import web
2+
import server
3+
from .flow_manager import FlowManager
4+
from .downloader import download_update_flows
5+
from .constants import FLOWMSG, logger
6+
7+
def setup_server() -> None:
8+
try:
9+
server_instance = server.PromptServer.instance
10+
except Exception as e:
11+
logger.error(f"{FLOWMSG}: Failed to get server instance: {e}")
12+
return
13+
14+
download_update_flows()
15+
16+
try:
17+
FlowManager.setup_app_routes(server_instance.app)
18+
except Exception as e:
19+
logger.error(f"{FLOWMSG}: Failed to set up app routes: {e}")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "comfyui-disty-flow"
33
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."
4-
version = "0.4.5"
4+
version = "0.4.6"
55
license = {file = "LICENSE"}
66

77
[project.urls]

web/core/js/common/components/footer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(function() {
22
async function getVersion() {
33
try {
4-
const response = await fetch('/api/flow-version');
4+
const response = await fetch('/flow/api/flow-version');
55
if (!response.ok) {
66
throw new Error(`HTTP error! status: ${response.status}`);
77
}

0 commit comments

Comments
 (0)