mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-24 12:01:16 -03:00
Compare commits
2 Commits
138024aefe
...
6a4fd020dc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a4fd020dc | ||
|
|
7a23040452 |
@@ -33,6 +33,7 @@ from .utils.example_images_migration import ExampleImagesMigration
|
|||||||
from .services.websocket_manager import ws_manager
|
from .services.websocket_manager import ws_manager
|
||||||
from .services.example_images_cleanup_service import ExampleImagesCleanupService
|
from .services.example_images_cleanup_service import ExampleImagesCleanupService
|
||||||
from .middleware.csp_middleware import relax_csp_for_remote_media
|
from .middleware.csp_middleware import relax_csp_for_remote_media
|
||||||
|
from .middleware.error_middleware import api_json_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -76,6 +77,11 @@ class LoraManager:
|
|||||||
"""Initialize and register all routes using the new refactored architecture"""
|
"""Initialize and register all routes using the new refactored architecture"""
|
||||||
app = PromptServer.instance.app
|
app = PromptServer.instance.app
|
||||||
|
|
||||||
|
# Register JSON error middleware for /api/* routes as the outermost
|
||||||
|
# middleware so it catches errors from all other middlewares.
|
||||||
|
if api_json_error not in app.middlewares:
|
||||||
|
app.middlewares.insert(0, api_json_error)
|
||||||
|
|
||||||
if relax_csp_for_remote_media not in app.middlewares:
|
if relax_csp_for_remote_media not in app.middlewares:
|
||||||
# Ensure CSP relaxer executes after ComfyUI's block_external_middleware so it can
|
# Ensure CSP relaxer executes after ComfyUI's block_external_middleware so it can
|
||||||
# see and extend the restrictive header instead of being overwritten by it.
|
# see and extend the restrictive header instead of being overwritten by it.
|
||||||
|
|||||||
71
py/middleware/error_middleware.py
Normal file
71
py/middleware/error_middleware.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
"""JSON error middleware for API routes.
|
||||||
|
|
||||||
|
Ensures all responses to /api/* requests return valid JSON that the
|
||||||
|
browser-extension frontend can JSON.parse() without crashing, even when
|
||||||
|
the route does not exist (404) or the handler raises an exception (500).
|
||||||
|
|
||||||
|
Extension consumers call response.json() unconditionally — an HTML error
|
||||||
|
page causes ``SyntaxError: unexpected end of data`` that leaks into the
|
||||||
|
popup UI as a toast notification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Awaitable, Callable
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@web.middleware
|
||||||
|
async def api_json_error(
|
||||||
|
request: web.Request,
|
||||||
|
handler: Callable[[web.Request], Awaitable[web.Response]],
|
||||||
|
) -> web.Response:
|
||||||
|
"""Return JSON ``{"success": false, "error": "..."}`` for API errors.
|
||||||
|
|
||||||
|
Only intercepts paths starting with ``/api/`` — all other routes
|
||||||
|
(frontend pages, static files, WebSocket upgrades) pass through
|
||||||
|
unchanged.
|
||||||
|
"""
|
||||||
|
if not request.path.startswith("/api/"):
|
||||||
|
return await handler(request)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await handler(request)
|
||||||
|
return response
|
||||||
|
except web.HTTPException as exc:
|
||||||
|
# Let redirects (301, 302, 307, 308) propagate — they are not errors.
|
||||||
|
if exc.status < 400:
|
||||||
|
raise
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
"API %s %s returned HTTP %d: %s",
|
||||||
|
request.method,
|
||||||
|
request.path,
|
||||||
|
exc.status,
|
||||||
|
exc.reason,
|
||||||
|
)
|
||||||
|
|
||||||
|
return web.json_response(
|
||||||
|
{"success": False, "error": f"{exc.status}: {exc.reason}"},
|
||||||
|
status=exc.status,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error(
|
||||||
|
"API %s %s raised unhandled exception: %s",
|
||||||
|
request.method,
|
||||||
|
request.path,
|
||||||
|
exc,
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return web.json_response(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"error": f"500: Internal Server Error ({type(exc).__name__})",
|
||||||
|
},
|
||||||
|
status=500,
|
||||||
|
)
|
||||||
@@ -11,7 +11,7 @@ from ..metadata_collector.metadata_processor import MetadataProcessor
|
|||||||
from ..metadata_collector import get_metadata
|
from ..metadata_collector import get_metadata
|
||||||
from ..utils.constants import CARD_PREVIEW_WIDTH
|
from ..utils.constants import CARD_PREVIEW_WIDTH
|
||||||
from ..utils.exif_utils import ExifUtils
|
from ..utils.exif_utils import ExifUtils
|
||||||
from ..utils.utils import calculate_recipe_fingerprint
|
from ..utils.utils import calculate_recipe_fingerprint, sanitize_folder_name
|
||||||
from PIL import Image, PngImagePlugin
|
from PIL import Image, PngImagePlugin
|
||||||
import piexif
|
import piexif
|
||||||
import logging
|
import logging
|
||||||
@@ -309,12 +309,14 @@ class SaveImageLM:
|
|||||||
filename = filename.replace(segment, str(h))
|
filename = filename.replace(segment, str(h))
|
||||||
elif key == "pprompt" and "prompt" in metadata_dict:
|
elif key == "pprompt" and "prompt" in metadata_dict:
|
||||||
prompt = metadata_dict.get("prompt", "").replace("\n", " ")
|
prompt = metadata_dict.get("prompt", "").replace("\n", " ")
|
||||||
|
prompt = sanitize_folder_name(prompt)
|
||||||
if len(parts) >= 2:
|
if len(parts) >= 2:
|
||||||
length = int(parts[1])
|
length = int(parts[1])
|
||||||
prompt = prompt[:length]
|
prompt = prompt[:length]
|
||||||
filename = filename.replace(segment, prompt.strip())
|
filename = filename.replace(segment, prompt.strip())
|
||||||
elif key == "nprompt" and "negative_prompt" in metadata_dict:
|
elif key == "nprompt" and "negative_prompt" in metadata_dict:
|
||||||
prompt = metadata_dict.get("negative_prompt", "").replace("\n", " ")
|
prompt = metadata_dict.get("negative_prompt", "").replace("\n", " ")
|
||||||
|
prompt = sanitize_folder_name(prompt)
|
||||||
if len(parts) >= 2:
|
if len(parts) >= 2:
|
||||||
length = int(parts[1])
|
length = int(parts[1])
|
||||||
prompt = prompt[:length]
|
prompt = prompt[:length]
|
||||||
@@ -328,6 +330,7 @@ class SaveImageLM:
|
|||||||
model = "model_unavailable"
|
model = "model_unavailable"
|
||||||
else:
|
else:
|
||||||
model = os.path.splitext(os.path.basename(model_value))[0]
|
model = os.path.splitext(os.path.basename(model_value))[0]
|
||||||
|
model = sanitize_folder_name(model)
|
||||||
if len(parts) >= 2:
|
if len(parts) >= 2:
|
||||||
length = int(parts[1])
|
length = int(parts[1])
|
||||||
model = model[:length]
|
model = model[:length]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from py.middleware.cache_middleware import cache_control
|
from py.middleware.cache_middleware import cache_control
|
||||||
|
from py.middleware.error_middleware import api_json_error
|
||||||
from py.utils.settings_paths import ensure_settings_file
|
from py.utils.settings_paths import ensure_settings_file
|
||||||
|
|
||||||
# Set environment variable to indicate standalone mode
|
# Set environment variable to indicate standalone mode
|
||||||
@@ -157,7 +158,7 @@ class StandaloneServer:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = web.Application(
|
self.app = web.Application(
|
||||||
logger=logger,
|
logger=logger,
|
||||||
middlewares=[cache_control],
|
middlewares=[api_json_error, cache_control],
|
||||||
client_max_size=256 * 1024 * 1024,
|
client_max_size=256 * 1024 * 1024,
|
||||||
handler_args={
|
handler_args={
|
||||||
"max_field_size": HEADER_SIZE_LIMIT,
|
"max_field_size": HEADER_SIZE_LIMIT,
|
||||||
|
|||||||
Reference in New Issue
Block a user