fix(webui): globally register correct MIME types for static assets (#5190)

On Windows, mimetypes.guess_type() reads the Content Type value from
HKEY_CLASSES_ROOT\.js (and other extensions) in the registry, which is
commonly set to text/plain because .js is associated with Windows Script
Host rather than web JavaScript. The registry value overrides Python's
built-in mapping and causes browsers to reject ES module scripts.

Fix by explicitly registering correct MIME types via mimetypes.add_type()
at module import time for common web static extensions (.js, .mjs, .css,
.html, .json, .svg, .wasm). Using strict=True ensures the values replace
the registry-backed standard mappings used by mimetypes.guess_type(). This
benefits all callers of mimetypes.guess_type() in the gateway process, not
just _serve_static.

Closes #5190

Co-authored-by: amkile <44280409+amkile@users.noreply.github.com>
This commit is contained in:
yang1 2026-07-31 14:58:16 +08:00 committed by chengyongru
parent 08fe9f7b3a
commit f7a6bc2d21

View File

@ -116,6 +116,30 @@ from nanobot.webui.workspaces import WebUIWorkspaceController
_SLOW_WEBUI_HTTP_LOG_MS = 1_000
_AUTOMATION_VALUES_HEADER = "X-Nanobot-Automation-Values"
# Fix for #5190: On Windows, mimetypes.guess_type() reads the registry key
# HKEY_CLASSES_ROOT\.js\Content Type, which is commonly set to 'text/plain'
# because .js is associated with Windows Script Host rather than web JavaScript.
# That registry value overrides Python's built-in mapping and causes browsers to
# reject ES module scripts with:
# Failed to load module script: Expected a JavaScript-or-Wasm module script
# but the server responded with a MIME type of "text/plain".
# We explicitly register correct MIME types for common web static assets here
# (module-import time) so all callers of mimetypes.guess_type() in this process
# benefit, regardless of host registry configuration.
_MIME_FIXES: dict[str, str] = {
".js": "application/javascript",
".mjs": "application/javascript",
".css": "text/css",
".html": "text/html",
".json": "application/json",
".svg": "image/svg+xml",
".wasm": "application/wasm",
}
for _ext, _ctype in _MIME_FIXES.items():
mimetypes.add_type(_ctype, _ext, strict=True)
if TYPE_CHECKING:
from nanobot.bus.queue import MessageBus
from nanobot.channels.websocket.runtime import WebSocketConfig
@ -123,7 +147,6 @@ if TYPE_CHECKING:
from nanobot.session.manager import SessionManager
from nanobot.triggers.local_store import LocalTriggerStore
def _decode_api_key(raw_key: str) -> str | None:
key = unquote(raw_key)
_api_key_re = re.compile(r"^[A-Za-z0-9_:.-]{1,128}$")