Skip to content

[bug] #1940

Open
Open

Description

@195070811

Octop 桌面版(Windows)卡在"加载中…"无法进入首次运行向导

影响版本:v1.0.2b6(beta,Windows amd64 桌面版);稳定版 v1.0.1 同样依赖系统 mimetypes,理论上同样受影响。
客户端:桌面版(Wails v3 + WebView2)。服务端:内嵌 Python 运行时,监听 127.0.0.1:8088。
一句话结论:Octop 服务端在 Windows 上用系统 mimetypes 推断前端 .js 的 MIME,当注册表把 .js 登记成 text/plain 时,所有 ES module 被浏览器拒载,桌面壳与浏览器都永久卡在启动画面。


一、现象

  1. 安装并启动 Octop 桌面版(Windows amd64)。
  2. 窗口永久停在"加载中…",不进入首次运行配置向导。
  3. 即使进程重启、甚至重装,依然卡住。
  4. 用浏览器直接打开 http://127.0.0.1:8088 也卡在同一个"加载中…"画面。

二、复现步骤(可稳定复现)

前置条件(任一即可触发):

  • 目标 Windows 机器的注册表 HKEY_CLASSES_ROOT\.js(或 HKLM\SOFTWARE\Classes\.js)的 Content Type 值为 text/plain;或
  • 该值缺失,且本机 Python mimetypes 对 .js 的默认回退为 text/plain(部分 Windows 环境如此)。

说明:.js 被登记为 text/plain 并非罕见。某些编辑器、IDE、下载工具、或手动改过文件关联/默认程序的机器上会出现。这意味着该问题不是个别现象,而是会在多台 Windows PC 上随机触发。

复现:

  1. 在符合上述前置条件的 Windows 上,安装并启动 Octop 桌面版。
  2. 观察桌面窗口 → 卡在"加载中…"。
  3. 打开浏览器 DevTools 控制台 → 看到报错:
    Failed to load module script: Expected a JavaScript-or-Wasm module script but the server
    responded with a MIME type of "text/plain".
    source: http://127.0.0.1:8088/assets/index.<hash>.js
    
  4. curl -I http://127.0.0.1:8088/assets/index.<hash>.js 可见响应头:
    content-type: text/plain; charset=utf-8
    
    (而 .css / .json / .svg 均返回正确类型,唯独 .js 错。)

三、根因分析

  • Octop 前端资源由内嵌的 Python 服务端作为静态文件托管。服务端用 Python 标准库 mimetypes 推断 Content-Type。
  • 在 Windows 上,mimetypes 会读取注册表 HKCR\<ext>\Content Type 来决定 MIME。当它从注册表(或被注册表污染后的默认值)拿到 .js → text/plain 时,就会把所有 .js 以 text/plain 发出。
  • 现代浏览器对 <script type="module"> 有严格的 MIME 嗅探:要求响应是 JavaScript MIME(如 text/javascript、application/javascript),收到 text/plain 会直接拒绝执行。
  • 结果:index.html 能返回(HTML 正常),但所有 ES module 脚本被拒 → React 永远无法挂载 → 启动画面 #octop-boot("加载中…")不被移除 → 永久卡住。
  • 这与桌面壳(WebView2)无关:用全新 profile 的 Edge 无头浏览器打开同一地址,同样卡在"加载中…",证明故障在服务端 MIME,不在客户端。

关键证据

请求 服务端 Content-Type 结果
/(index.html) text/html ✅ 正常
/assets/*.js text/plain; charset=utf-8 ❌ 故障点
/assets/*.css text/css ✅ 正常
/manifest.json application/json ✅ 正常

本机 Python 验证:mimetypes.guess_type('x.js') == ('text/plain', None),且来源确认为注册表 HKLM\SOFTWARE\Classes\.js 的 Content Type = text/plain。给用户分支 HKCU\Software\Classes\.js 写入 Content Type = text/javascript 后,guess_type 即返回 text/javascript,服务端随即正确返回 text/javascript。

四、第二个连环坑:WebView2 磁盘缓存

即使修复了注册表 MIME,仅重启进程仍无法恢复,原因在于 WebView2 的磁盘缓存:

  • 首次启动时服务端还在发 text/plain,WebView2 已将 .js 连同错误类型缓存到磁盘,响应头含 cache-control: max-age=31536000, immutable 与 ETag。
  • 修复注册表并重启 Octop 进程后,WebView2 重新请求 .js 时携带旧 ETag → 服务端回 304 Not Modified → WebView2 继续使用缓存里的 text/plain 旧版本 → 仍被浏览器拒载 → 仍卡"加载中"。
  • 必须手动删除 WebView2 磁盘缓存目录才能恢复。Octop 桌面壳的缓存位于:
    %APPDATA%\<Octop.exe 名>\EBWebView\Default\Cache\Cache_Data
    
    (例如 C:\Users\<user>\AppData\Roaming\Octop.exe\EBWebView)。

这意味着普通用户即使"修好了根因",也基本不可能自己推断出"还要清 WebView2 缓存",体验上等于修不好的死循环。

五、连带体验问题:首启密码不可发现

  • 首次运行向导的管理员密码写入 C:\Users\<user>\octop-login.txt。
  • 但桌面壳卡在"加载中…"时,用户进不去任何界面,也没有任何 UI 提示"密码已生成在 octop-login.txt",导致排查时连"该去哪找密码"都不知道。
  • 建议:桌面壳在卡住/出错时,至少在窗口内或日志里提示该文件路径;或向导密码改用 UI 内输入而非隐藏到文件。

六、建议修复

主因(必须修):不要依赖系统 mimetypes/注册表 推断前端静态资源 MIME。

推荐任选其一:

  1. 在静态文件托管处显式、硬编码前端资源的正确 MIME 映射,例如:
    STATIC_MIMES = {
        ".js":  "text/javascript; charset=utf-8",
        ".mjs": "text/javascript; charset=utf-8",
        ".css": "text/css; charset=utf-8",
        ".json": "application/json; charset=utf-8",
        ".svg": "image/svg+xml",
        ".wasm": "application/wasm",
    }
    服务端按扩展名查这张表发出 Content-Type,完全不调用 mimetypes。
  2. 或在进程启动早期强制覆盖:
    import mimetypes
    mimetypes.add_type("text/javascript", ".js")
    mimetypes.add_type("text/javascript", ".mjs")
    (但方案 1 更稳,因为不依赖注册表是否被后续污染。)
  3. 改用自带正确 MIME 的静态服务器(如 whitenoise 或框架的 send_file(..., mimetype=...))。

缓存坑(建议修):

  • 前端 assets 使用 hash 文件名(Octop 已这样做,如 index.<hash>.js)。既然文件名带 hash,内容变更即换名,无需 immutable 一年缓存;建议把 cache-control 调成温和值,或在桌面壳检测到启动失败时自动清理 WebView2 缓存。
  • 桌面壳提供一个"清除缓存/重置"入口(托盘右键或启动参数),避免用户手动找 %APPDATA% 目录。

体验(建议修):

  • 卡在加载中/首次运行时,UI 内明确提示向导密码路径(octop-login.txt)与排查方向。

七、环境信息

  • OS:Windows 10 / 11(x64,amd64)
  • Octop 版本:v1.0.2b6(beta);桌面包 Octop-desktop-windows-amd64-*.exe
  • 客户端:Wails v3 + Microsoft Edge WebView2(Runtime 版本 148.x)
  • 服务端:内嵌 Python 运行时(随桌面包分发),监听 127.0.0.1:8088
  • 数据目录:C:\Users\<user>\.octop\
  • 触发条件:本机注册表 HKCR\.js / HKLM\SOFTWARE\Classes\.js 的 Content Type 为 text/plain(或缺失导致默认回退为 text/plain)

八、临时绕过(供其他遇到者参考)

  1. 用户级覆盖注册表(免管理员):
    New-ItemProperty -Path "HKCU:\Software\Classes\.js" -Name "Content Type" -Value "text/javascript" -PropertyType String -Force
  2. 彻底退出 Octop(结束 Octop.exe 与内嵌 python.exe 进程)。
  3. 删除 WebView2 缓存目录 %APPDATA%\<Octop.exe 名>\EBWebView。
  4. 重新启动 Octop。

英文摘要(如向上游提英文 issue 可附上)

Title: Desktop app (Windows) stuck on "Loading…" — server serves .js with text/plain MIME due to Windows registry mimetypes

Octop's embedded Python server uses mimetypes.guess_type() to set Content-Type for frontend static assets. On Windows, mimetypes reads the registry (HKCR\.js → Content Type). When that value is text/plain (which happens on many Windows machines, e.g. after certain editors/tools touch file associations), all ES module .js files are served as text/plain. Browsers strictly reject text/plain module scripts, so React never mounts and the desktop shell (WebView2) — and even a plain browser at 127.0.0.1:8088 — stay stuck on the "加载中…" boot screen forever.

Repro: install/run Octop desktop on a Windows box whose registry .js = text/plain → window hangs on boot → console shows Failed to load module script ... MIME type of "text/plain".

Secondary trap: WebView2 caches the bad .js on disk (immutable, 1y). Fixing the registry + restarting the process is not enough — the WebView2 disk cache (%APPDATA%\<Octop.exe>\EBWebView) must be deleted too.

Fix建议: hardcode frontend MIME map (.js→text/javascript) instead of relying on system mimetypes; avoid immutable caching for hashed assets; expose a "clear cache" entry in the desktop shell; surface the first-run wizard password path in the UI.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions