Skip to content

fix: Linux 包带上各发行版的 Prisma 引擎,初始化失败时界面给出提示 (#86) - #93

Merged
haywolf merged 7 commits into
masterfrom
fix/86-prisma-linux-engines
Nov 5, 2026
Merged

haywolf merged 7 commits into
masterfrom
fix/86-prisma-linux-engines

Conversation

@haywolf

@haywolf haywolf commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #86

改动

  • Prisma 引擎:新增 scripts/prisma-engines.mjs(列出各架构需要的引擎)和 scripts/prisma-cli.mjs(按构建机的架构设置 PRISMA_BINARY_TARGETS);schema.prisma 改成 binaryTargets = env("PRISMA_BINARY_TARGETS")。Linux x64 和 arm64 包会带上 debian、rhel 两类系统下 openssl-1.1.x 和 3.0.x 的引擎;npx 模式只生成本机用的引擎。
  • 发包校验:verify-package-arch.mjs 要求 Linux 包带齐全部目标引擎;新增 verify:package:distros,用 Docker 在 Fedora 44、Rocky 8、Ubuntu 20.04 上加载包,并加进 ci.yml 和 release.yml。容器挂载整个 scripts/ 目录。
  • 初始化失败提示:initDatabase 返回 {success, error, databasePath};新增 app:getDatabaseStatus IPC 和常驻提示 DatabaseStatusAlert(6 种语言);数据库不可用时不启动 worker。迁移失败时,界面显示可复制的数据库路径和 4 步「备份后重建」说明(移走文件而不是删除,可以撤回);Prisma 引擎缺失仍显示原来的提示。
  • 迁移原子化(issue 里没写的问题):每个迁移放在一个事务里,失败就停下并报出迁移名。修复了全新数据库首次启动时迁移随机失败、库只迁移了一半的问题。
  • 用 @napi-rs/canvas 替换 sharp(issue 里没写的问题):sharp 在 Fedora 44 的 Electron 里会段错误。
    • 新增 ImageReencoder,ImageSplitter 改成用 canvas 解码后重新编码:JPEG 和 WebP 用质量 80(和 sharp 默认一致),PNG 无损。这样 EXIF、PNG 的 eXIf/tEXt 等元数据都会被清除,图片按 EXIF 方向转正。
    • 解码失败或图片被截断时,任务直接失败,不会退回去发原图。
    • ModelService 发送前会检查元数据标记,有就重新编码,覆盖升级前留下的旧任务重试。
    • PDF 页面不重新编码。
    • @napi-rs/canvas 精确锁定在 1.0.11:0.1.88 解码损坏图片时会段错误,1.0.12 和 1.0.13 在 Electron 里一加载就崩。
    • fix: 打包版 pdf.js 的 CMap 和标准字体路径不再依赖 cwd (#85) #92 的渲染检查改用 canvas 统计暗像素(scripts/fixtures/count-dark-pixels.cjs),删掉了 checkSharp。
  • README(6 种语言)和设计文档同步更新。

测试

  • npm run test:unit:1175 个用例全部通过;npm run test:renderer:308 个通过;typecheck、lint 通过;verify:package(linux-x64)通过。
  • CI 全部通过,包括 Fedora 44、Rocky 8、Ubuntu 20.04 的 Docker 校验。
  • 打包应用端到端测试(xvfb + 真实 Electron):
    • 4000×3000 带 GPS 的图片实际发送 3,291,021 字节,不含 Exif(master 用 sharp 时是 3,290,529)。
    • 截断的图片:任务失败,应用没有崩溃。
    • 半迁移的库:界面显示正确的路径和恢复步骤。
  • 撤回验证:12 组撤回都有测试按预期失败。

需要注意

  • 以前迁移了一半的库会显示报错和恢复步骤,但不会自动修复。
  • 直接跑 npx prisma generate 会报缺环境变量,要改用 npm run generate。
  • 图片现在会按 EXIF 方向转正,master 不会。
  • PDF 页面不再经过 sharp 压缩,一页密集文字的请求大了约 54%。

未验证项

  • macOS、Windows 和 arm64 上 canvas 1.0.11 在 Electron 里能否正常工作还没实测,发版前需要在各平台跑一次 verify:package。
  • 大库跨版本升级时启动期没有反馈,这次不处理,建议另开 issue。

A failed database initialization was swallowed: the app kept running with
every database feature broken and nothing telling the user why (#86).

- initDatabase returns its outcome and runs once, and the renderer shows a
  persistent alert with the error through app:getDatabaseStatus.
- Services that need the database don't start when it's unavailable.
- runMigrations throws on the first failure instead of logging it and going
  on to apply later migrations on top of an incomplete schema.
- Each migration runs in one transaction. Its statements could otherwise land
  on different pooled connections, whose stale schema cache made a RENAME after
  a DROP TABLE fail at random with "there is already another table", leaving
  the database half migrated: 19 of 640 replays failed before, 0 of 640 after.
- The window opens after the migrations, so that no query sees them half done.
Linux packages only held the engine of the Ubuntu build machine, while Prisma
picks one from the distro family and libssl of the user's machine. On Fedora,
RHEL, openSUSE and any OpenSSL 1.1 distro it found none (#86).

- Generate debian/rhel x OpenSSL 1.1/3.0 engines on x64 and both OpenSSL arm64
  engines on arm64. binaryTargets comes from PRISMA_BINARY_TARGETS, set by
  scripts/prisma-cli.mjs, so a package never holds engines of the other arch.
- verify:package requires each of them, and verify:package:distros checks in
  containers that Fedora, Rocky 8 and Ubuntu 20.04 load the engine meant for
  them. Both CI and the release workflow run it.
- Leave the unused engine copies of the Prisma CLI out of the package.
- Remove sharp, which segfaults in Electron on Fedora 44: its libvips bundles
  glib while Electron links the system's. Conversion only used it to read the
  page image, so it now reads the file. EXIF is no longer stripped from
  uploaded images as a side effect.
@haywolf

haywolf commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

一、审查结论

不通过。 去掉 sharp 后,用户上传图片里的 GPS 和设备信息会原样发给模型服务商,属于高级隐私问题,PR 描述里写明了风险也不能降级。Prisma 多引擎配置和迁移事务本身正确,未发现阻塞问题;但 #92 与本 PR 存在 git 报不全的语义冲突,后合入的一方必须处理。

二、审查明细

缺陷评级 文件:行号 缺陷类型 带来的影响 建议方案
高(已复现) src/core/application/services/ModelService.ts:62、src/core/infrastructure/adapters/split/ImageSplitter.ts:61 隐私回退:元数据不再被清除 真实走 ImageSplitter 和 transformImageMessage,载荷与原 JPEG 逐字节相同,可解析出 GPS(39°54'27"N, 116°23'30"E)和设备厂商。master 的 sharp(path).toBuffer() 输出无 EXIF。PNG 的 eXIf 同样会泄露。影响所有平台和服务商,界面也没有提示用户。PDF 路径未改动,其页面图片不走 ImageSplitter。 在 ImageSplitter 中用已打包的 @napi-rs/canvas 解码并重编码,避免波及 PDF 页。已在普通 Node 中验证 GPS 会被清除,Electron 内还需实测。canvas 会按 orientation 转正(400×200 变 200×400),master 则是丢弃方向不旋转。补一条断言载荷不含 Exif 的测试。
中(体积已复现;服务商拒绝为读代码推断) src/core/application/services/ModelService.ts:62 行为回退:隐式重编码消失,请求变大 合成的 4000×3000、q97 JPEG:master 发送 3.37 MB(base64 4.49 MB),本 PR 发送 7.86 MB(base64 10.48 MB)。原本能过服务商单图上限(如 Anthropic 约 5 MB,未实测调用)的图片可能被拒,页面转换失败。 与上一条一并修复,重编码质量取接近 sharp 默认值,并用大图验证。
中(已复现) scripts/package-load-check.cjs、scripts/verify-package-distros.mjs:48;#92 的 scripts/fixtures/pdf-render-check.cjs:21-22、PDFPageRenderer.integration.test.ts:6 与 #92 的语义冲突:git 只报 package-load-check.cjs 合并后三处会坏:① 集成测试报 Failed to load url sharp。② 打包检查的 loadFromApp('sharp') 也依赖已移除的模块(读代码推断)。③ 容器只挂载 /load-check.cjs,加载 ./fixtures/... 时报 MODULE_NOT_FOUND,发行版校验失败。如果为过 CI 保留 sharp,又会带回 Fedora 段错误风险(读代码推断)。 后合入方:把 countDarkPixels 改用 @napi-rs/canvas;删除 checkSharp;保留 return loadedPrismaEngine();容器改为挂载整个 scripts/。
中(已复现) src/core/infrastructure/db/Migration.ts:209、src/renderer/locales/zh-CN/common.json:38(6 种语言) 升级路径无法恢复 ① master 遗留的半迁移库(有 new_Task、无 Task)每次启动都在同一迁移失败,“请重启应用”无效。② 表结构完好但丢了一条迁移记录的库,master 仍继续运行,现在会被判定初始化失败,worker 和预设都不启动。这一情况比较少见,但属于新回归,PR 也没提到。 最小修复:文案给出数据库路径和“备份后重建”的恢复步骤,不再只提示重启。半迁移库的自动修复可另开 issue。
低(读代码推断) src/main/index.ts:438 启动期无反馈 窗口要等迁移完成才创建,单个迁移事务最长 5 分钟。其间没有界面,点 Dock 或再次启动都不会出现窗口,用户可能以为应用卡死。常规启动影响很小,只有大库跨版本升级才明显。 大库迁移时显示启动页,或先开窗口并暂缓渲染进程的查询。
低(读代码推断) README.md:120 等 6 个 README;docs/CONVERTER_WORKER_DESIGN.md:74、docs/TASK_STATE_DESIGN.md:297 文档不一致 README 仍把 Sharp 列为技术栈。设计文档仍写 npx prisma migrate dev,缺环境变量时应会报错;裸 prisma generate 的报错已实测。 更新 README,并把命令改为 npm run migrate:dev。
低(已复现) src/core/application/services/ModelService.ts:1-6(与 #91 冲突);#91 的 ProviderEndpoint.test.ts:7,18 与 #91 的冲突和清理遗漏 import 行有简单的文本冲突:保留 fs 和 providerSuffix,去掉 sharp。解决后相关测试全过。测试里残留的 vi.mock('sharp') 和注释不影响功能。 后合入方顺手删除残留。

重点项核查结果(未发现缺陷)

  • 迁移事务:
    • 回滚: 表重建迁移中途失败时,事务完整回滚,业务数据和迁移记录都不变。
    • 幂等: 排除故障后重试成功,再跑一次没有变化。
    • 稳定性: 全新库连续迁移 40 次,失败 0 次。
    • PRAGMA: foreign_keys 在事务内不生效,但当前 schema 没有外键,所以无影响。
  • Prisma 多引擎:
    • 环境变量: binaryTargets 在 generate 时已解析(fromEnvVar: null),运行时不依赖该变量。
    • 引擎覆盖: x64 生成了全部 4 个引擎,发行版映射也与清单对得上:rhel/fedora/suse 系对应 rhel,debian/arch 系对应 debian。
    • 命令行: 裸 prisma generate 按文档报错,bin/cli.js 使用的 ["native"] 可正常生成。
    • 包体积: Linux x64 未压缩约增加 60 MB,不算缺陷。
  • macOS activate 竞态: 不成立。首个窗口创建前 mainWindow 是 undefined,=== null 判断为假。

未验证

  • arm64 和 macOS、Windows 包未验证,PR 也未覆盖。
  • CI 的 ci job 在该 head 上通过,日志显示已执行 Docker 发行版校验;只覆盖 x64。
  • 我没有实际打包运行 Electron。

附:跑过的测试及结果

所有测试和复现都在 /tmp 的临时副本中进行,副本已删除,审查目录 git status 保持干净。副本的依赖用 npm install --ignore-scripts 安装。全量测试未跑,也没有重跑任何命令。

内容 结果
HEAD 相关单测 8 个文件:Migration、initDatabase、app.handler、ModelService、OllamaProtocol、prisma-engines、verify-package-distros、verify-package-arch 77/77 通过
EXIF 端到端复现(真实 Splitter 和 ModelService,未 mock) 确认泄露,载荷中可解析出 GPS
master 的 sharp 路径对比(JPEG、PNG)及重编码体积 sharp 输出无 EXIF,体积见上表
@napi-rs/canvas 修复方案验证(仅普通 Node) GPS 已清除,orientation 已应用
迁移场景(回滚、重试、半迁移库、丢记录、连续 40 次) 结果见上文:事务逻辑正确,升级路径问题成立
#93 + #91 解决冲突后:ProviderEndpoint、ModelService、OllamaProtocol 44/44 通过
#93 + #92 合并后:PDFPageRenderer.integration.test.ts 失败,缺少 sharp,与上表缺陷一致
单独运行合并后的 load-check MODULE_NOT_FOUND,与上表缺陷一致
渲染进程测试(DatabaseStatusAlert、Layout) 未运行,这部分只读了代码

Removing sharp also removed the re-encoding it did on the way out: uploads
reached the model provider byte for byte, with their GPS position and device
details, and much larger (a 4000x3000 JPEG: 8.48 MB, was 3.29 MB).

- ImageSplitter re-encodes uploads through @napi-rs/canvas at sharp's default
  quality, and fails rather than fall back to the original. The Exif
  orientation is applied to the pixels, so photos stay upright.
- ModelService re-encodes any page file that may still hold metadata. Retrying
  a task reuses its page files, and those of older versions are plain copies.
- Reject truncated images. Unlike sharp, the decoder draws what's there and
  leaves the rest blank, so a page would silently lose its text.
- Pin @napi-rs/canvas to 1.0.11. Up to 1.0.3 it segfaults on damaged images,
  which would crash the app on each start, and 1.0.12 and 1.0.13 segfault in
  Electron as soon as they load. The load check now covers both.
- Mount all of scripts/ in the distro containers: the load check requires
  fixtures/count-dark-pixels.cjs, which the PDF rendering check of #92 can
  share instead of its use of sharp.
A database left half migrated by an older version, or one that lost the record
of a migration, fails on every start, while the alert only said to restart.

initDatabase now also returns the database path when the file is what's failing,
and the alert gives it with the steps to back it up and rebuild it. A missing
engine keeps the old text, since a new database would fail as well.
…-engines

Brings in #91 and #92, both of which still relied on sharp.

- pdf-render-check.cjs and the PDF rendering test count pixels through
  count-dark-pixels.cjs and @napi-rs/canvas.
- package-load-check.cjs: no checkSharp, and both checkPdfRendering and the
  Prisma engine it reports.
- ModelService: no sharp, keeping providerSuffix from #91 and the re-encoding.
- ProviderEndpoint.test: drop its mock of sharp.
@haywolf
haywolf merged commit 352eb2e into master Nov 5, 2026
2 checks passed
@haywolf
haywolf deleted the fix/86-prisma-linux-engines branch November 5, 2026 02:41
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.

Linux 安装包只带 debian-openssl-3.0.x 的 Prisma 引擎,Fedora/RHEL 及 OpenSSL 1.1 系统数据库不可用

1 participant