"""Browsable API documentation for the ComfyUI HTTP API. Renders the repository's ``openapi.yaml`` with Redoc. Redoc is used rather than Swagger UI because it has no request-execution feature at all: the local server is unauthenticated by default, so a docs page that could fire requests would give one-click access to destructive endpoints such as ``/api/interrupt``, ``/api/free`` and ``DELETE /api/userdata/{file}``. The viewer bundle is loaded from a CDN, so the page needs outbound network access to render. Installs without it still get the raw spec from ``/openapi.yaml``; the fallback notice below points there. """ import os from aiohttp import web # openapi.yaml lives next to server.py at the repository root. Resolve it from # __file__ rather than the cwd: ComfyUI is routinely launched from other # directories and through wrappers, and a relative path would 404 unpredictably. SPEC_PATH = os.path.join( os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "openapi.yaml" ) # Pinned to an exact version rather than a floating tag so a CDN-side release # cannot change what this page executes. # # TODO: add an integrity="sha384-..." attribute. The pinned path is immutable, # so SRI is worth having; the hash simply could not be computed where this was # written (no outbound network), and a wrong hash fails the page closed. REDOC_BUNDLE_URL = "https://cdn.jsdelivr.net/npm/redoc@2.5.0/bundles/redoc.standalone.js" # The spec URL is deliberately relative. add_routes() re-registers every route # under an /api prefix, so this page is reachable at both /api-docs and # /api/api-docs; a relative URL resolves to the sibling spec in either case. SPEC_URL = "openapi.yaml" # Substituted with str.replace rather than str.format/f-string so the CSS braces # below stay literal and a future style edit does not have to double them. API_DOCS_HTML = """ ComfyUI API Reference

API docs viewer unavailable

The documentation viewer is loaded from a CDN and could not be reached. This is expected on an offline or air-gapped install.

The specification itself is served locally and needs no network access: openapi.yaml. Render it with any local viewer, for example npx @redocly/cli preview-docs openapi.yaml.

""".replace("__SPEC_URL__", SPEC_URL).replace("__BUNDLE_URL__", REDOC_BUNDLE_URL) def add_api_docs_routes(routes: web.RouteTableDef) -> None: """Register the spec and docs-page routes on the given route table. Registering on PromptServer's route table (rather than on the app directly) matters twice over: the table is added before the ``web.static('/')`` catch-all that would otherwise shadow these paths, and every route in it is also re-registered under an ``/api`` prefix. """ @routes.get("/openapi.yaml") async def get_openapi_spec(request): if not os.path.isfile(SPEC_PATH): return web.Response(status=404, text="openapi.yaml not found") response = web.FileResponse(SPEC_PATH) response.headers["Content-Type"] = "application/yaml" # The cache_control middleware only special-cases js/css/images, so a # .yaml response falls through untouched. Without this, a user editing # the spec would keep getting a stale copy from the browser cache. response.headers["Cache-Control"] = "no-store, must-revalidate" return response @routes.get("/api-docs") async def get_api_docs(request): response = web.Response(text=API_DOCS_HTML, content_type="text/html") response.headers["Cache-Control"] = "no-store, must-revalidate" return response