Skip to content

Conversation

@tonyjohnvan
Copy link
Contributor

@tonyjohnvan tonyjohnvan commented Feb 9, 2026

Problem

Opening ui/studio.html via file:// protocol and calling the API at http://localhost:8001 fails with:

"Access to fetch at '…/release_task' from origin 'null' has been
blocked by CORS policy: Response to preflight request doesn't pass
access control check"

The browser sends a preflight OPTIONS request for the cross-origin POST, but neither the standalone FastAPI server (api_server.py) nor the Gradio-mounted API routes (api_routes.py) return the required Access-Control-Allow-Origin header.

Changes

  1. acestep/api_server.py

    • Import CORSMiddleware from fastapi.middleware.cors.
    • Add CORSMiddleware to the standalone FastAPI app immediately after creation (before the app starts, so add_middleware() works normally).
  2. acestep/gradio_ui/api_routes.py

    • Import CORSMiddleware from fastapi.middleware.cors.
    • Add _CORS_KWARGS dict with the shared CORS configuration.
    • Add _add_cors_middleware() for the normal pre-launch path (used by setup_api_routes_to_app).
    • Add _add_cors_middleware_post_launch() that patches the compiled middleware_stack directly, because Gradio's demo.launch() starts the Starlette app before setup_api_routes() is called, and Starlette raises "Cannot add middleware after an application has started" if add_middleware() is used post-launch.
    • Wire both setup functions to their respective CORS helpers.

CORS policy (applied uniformly in both files)

allow_origins : ["null", "http://localhost", "http://127.0.0.1"]
allow_origin_regex : ^https?://(localhost|127.0.0.1)(:\d+)?$
allow_methods : ["GET", "POST", "OPTIONS"]
allow_headers : ["Content-Type", "Authorization"]
allow_credentials : not set (defaults to False)

Benefits

  • studio.html (opened via file://, origin "null") can now call the API.
  • Any local dev server on localhost or 127.0.0.1 (any port) also works.
  • The Gradio + API path no longer crashes on startup.

Risks & mitigations

  • Origin "null" is allowed. The "null" origin is sent by file:// pages, sandboxed iframes, and some redirect flows. A malicious local HTML file could call the API if opened in the same browser. Mitigation: the API is bound to localhost by default, and API key auth (--api-key) is available for sensitive deployments.
  • If the server is exposed to the network (0.0.0.0 or --share), only localhost origins are allowed — external sites cannot call the API. However, the "null" origin exception still applies to any file:// page on the user's machine.
  • The post-launch middleware patching (_add_cors_middleware_post_launch) accesses app.middleware_stack directly, which is a Starlette internal. This could break if Starlette refactors its middleware compilation. Mitigation: guarded by an if app.middleware_stack is not None check and falls back to the normal add_middleware path otherwise.
  • allow_credentials is intentionally omitted (False). studio.html uses plain JSON fetch without cookies. If cookie-based auth is added later, this will need revisiting.

Summary by CodeRabbit

  • Bug Fixes
    • Resolved cross-origin resource sharing (CORS) issues, enabling browser-based frontends to access the API from localhost and file:// protocol origins.

Problem
-------
Opening ui/studio.html via file:// protocol and calling the API at
http://localhost:8001 fails with:

  "Access to fetch at '…/release_task' from origin 'null' has been
   blocked by CORS policy: Response to preflight request doesn't pass
   access control check"

The browser sends a preflight OPTIONS request for the cross-origin
POST, but neither the standalone FastAPI server (api_server.py) nor the
Gradio-mounted API routes (api_routes.py) return the required
Access-Control-Allow-Origin header.

Changes
-------
1. acestep/api_server.py
   - Import CORSMiddleware from fastapi.middleware.cors.
   - Add CORSMiddleware to the standalone FastAPI app immediately after
     creation (before the app starts, so add_middleware() works normally).

2. acestep/gradio_ui/api_routes.py
   - Import CORSMiddleware from fastapi.middleware.cors.
   - Add _CORS_KWARGS dict with the shared CORS configuration.
   - Add _add_cors_middleware() for the normal pre-launch path (used by
     setup_api_routes_to_app).
   - Add _add_cors_middleware_post_launch() that patches the compiled
     middleware_stack directly, because Gradio's demo.launch() starts the
     Starlette app before setup_api_routes() is called, and Starlette
     raises "Cannot add middleware after an application has started" if
     add_middleware() is used post-launch.
   - Wire both setup functions to their respective CORS helpers.

CORS policy (applied uniformly in both files)
----------------------------------------------
  allow_origins       : ["null", "http://localhost", "http://127.0.0.1"]
  allow_origin_regex  : ^https?://(localhost|127\.0\.0\.1)(:\d+)?$
  allow_methods       : ["GET", "POST", "OPTIONS"]
  allow_headers       : ["Content-Type", "Authorization"]
  allow_credentials   : not set (defaults to False)

Benefits
--------
- studio.html (opened via file://, origin "null") can now call the API.
- Any local dev server on localhost or 127.0.0.1 (any port) also works.
- The Gradio + API path no longer crashes on startup.

Risks & mitigations
--------------------
- Origin "null" is allowed. The "null" origin is sent by file:// pages,
  sandboxed iframes, and some redirect flows. A malicious local HTML
  file could call the API if opened in the same browser. Mitigation:
  the API is bound to localhost by default, and API key auth (--api-key)
  is available for sensitive deployments.
- If the server is exposed to the network (0.0.0.0 or --share), only
  localhost origins are allowed — external sites cannot call the API.
  However, the "null" origin exception still applies to any file://
  page on the user's machine.
- The post-launch middleware patching (_add_cors_middleware_post_launch)
  accesses app.middleware_stack directly, which is a Starlette internal.
  This could break if Starlette refactors its middleware compilation.
  Mitigation: guarded by an `if app.middleware_stack is not None` check
  and falls back to the normal add_middleware path otherwise.
- allow_credentials is intentionally omitted (False). studio.html uses
  plain JSON fetch without cookies. If cookie-based auth is added later,
  this will need revisiting.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

The changes enable CORS (Cross-Origin Resource Sharing) support for the FastAPI application across two modules. CORSMiddleware is integrated into both the main API server and the Gradio UI API routes, allowing browser-based frontends to access the API from localhost and file:// origins.

Changes

Cohort / File(s) Summary
CORS Middleware Configuration
acestep/api_server.py, acestep/gradio_ui/api_routes.py
Added CORSMiddleware imports and configuration to enable cross-origin requests. Defines allowed origins (localhost variants, null), origin regex patterns, HTTP methods (GET, POST, OPTIONS), and headers. Implements utility functions to apply CORS at different application lifecycle stages (early setup and post-launch).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A little hop and skip through borders,
CORS whispers cross-origin orders!
Localhost friends now play together,
Browser and API, linked forever! 🌐✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding CORS support for browser-based frontends opened directly via file://. It accurately reflects the primary objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@acestep/gradio_ui/api_routes.py`:
- Around line 533-545: In _add_cors_middleware_post_launch, fix the
typographical dash in the inline comment "App hasn't built its stack yet – safe
to use the normal path" by replacing the EN DASH (U+2013) with a standard
hyphen-minus (U+002D) so it reads "App hasn't built its stack yet - safe to use
the normal path"; update the comment in that function accordingly.
🧹 Nitpick comments (1)
acestep/gradio_ui/api_routes.py (1)

516-525: Consider extracting shared CORS configuration.

The _CORS_KWARGS configuration is duplicated between api_server.py and this file. While acceptable for module independence, consider extracting to a shared constants module if these need to stay in sync.

Comment on lines +533 to +545
def _add_cors_middleware_post_launch(app):
"""Wrap an already-started app's middleware stack with CORS.

``add_middleware`` raises after Starlette has started, so we patch the
compiled middleware stack directly instead.
"""
from starlette.middleware.cors import CORSMiddleware as _CORSImpl

if app.middleware_stack is not None:
app.middleware_stack = _CORSImpl(app=app.middleware_stack, **_CORS_KWARGS)
else:
# App hasn't built its stack yet – safe to use the normal path
_add_cors_middleware(app)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Post-launch middleware patching approach is reasonable given constraints.

Wrapping app.middleware_stack directly is a pragmatic workaround for Gradio's post-launch app access pattern. The fallback to the normal path when the stack isn't built yet is a good safeguard.

One minor fix—line 544 contains an EN DASH () instead of a hyphen (-):

📝 Fix typographical character
-    # App hasn't built its stack yet – safe to use the normal path
+    # App hasn't built its stack yet - safe to use the normal path
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _add_cors_middleware_post_launch(app):
"""Wrap an already-started app's middleware stack with CORS.
``add_middleware`` raises after Starlette has started, so we patch the
compiled middleware stack directly instead.
"""
from starlette.middleware.cors import CORSMiddleware as _CORSImpl
if app.middleware_stack is not None:
app.middleware_stack = _CORSImpl(app=app.middleware_stack, **_CORS_KWARGS)
else:
# App hasn't built its stack yet safe to use the normal path
_add_cors_middleware(app)
def _add_cors_middleware_post_launch(app):
"""Wrap an already-started app's middleware stack with CORS.
``add_middleware`` raises after Starlette has started, so we patch the
compiled middleware stack directly instead.
"""
from starlette.middleware.cors import CORSMiddleware as _CORSImpl
if app.middleware_stack is not None:
app.middleware_stack = _CORSImpl(app=app.middleware_stack, **_CORS_KWARGS)
else:
# App hasn't built its stack yet - safe to use the normal path
_add_cors_middleware(app)
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 544-544: Comment contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF003)

🤖 Prompt for AI Agents
In `@acestep/gradio_ui/api_routes.py` around lines 533 - 545, In
_add_cors_middleware_post_launch, fix the typographical dash in the inline
comment "App hasn't built its stack yet – safe to use the normal path" by
replacing the EN DASH (U+2013) with a standard hyphen-minus (U+002D) so it reads
"App hasn't built its stack yet - safe to use the normal path"; update the
comment in that function accordingly.

@tonyjohnvan
Copy link
Contributor Author

tonyjohnvan commented Feb 9, 2026

@ChuxiJ I took a look into the ui and the api folder and found the original simple html won't be working by "click to open" mechanism. the API backend was not setup CORS for good, this temporally fix is allowing direct html file open to access the API features, more safer approach is to let user cd ./ui && python3 -m http.server <PORT> to host the HTML file in a simple web server. I did this change anyway since the doc allow user to directly open the file for simpler UI and API access without mentioning about the web server. But it's really up to you to pull the trigger to determine should we go with CORS or web server

(your IDE might have build in web server for you to "click to open" the HTML file directly? I used to use Intelij IDEA I know they does like that)

@ChuxiJ
Copy link
Contributor

ChuxiJ commented Feb 10, 2026

We don’t need to develop or design this part for now.
Let’s focus our recent development on device compatibility and the stable release when submitting PRs.
We’ll handle UI optimizations later in a targeted way.

By then, we’ll support more features to better cover workflows like Cover, Remix, and Edit, along with project refactoring and more complete tutorials.
Let’s set aside these issues that aren’t part of our current focus for the time being.

@ChuxiJ ChuxiJ merged commit 9a13d4c into ace-step:main Feb 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants