Skip to content

Comments

用sonnet4.6重写把后端改成nodejs#409

Open
hjyssg wants to merge 54 commits intodev-vibecodingfrom
nodejs-backend
Open

用sonnet4.6重写把后端改成nodejs#409
hjyssg wants to merge 54 commits intodev-vibecodingfrom
nodejs-backend

Conversation

@hjyssg
Copy link
Owner

@hjyssg hjyssg commented Feb 20, 2026

python不知道为什么会害windows狂删AppData。害我重装系统两次。
正好测试一下sonnet 4.6的实力。

@hjyssg
Copy link
Owner Author

hjyssg commented Feb 20, 2026

开始时间是0220的23:30,测试一下。一万多行 重写要多久。

@hjyssg hjyssg changed the title 用sonnet4.6重写把后端改成nodej 用sonnet4.6重写把后端改成nodejs Feb 20, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04435cc1bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 71 to 72
app.get("", listHistory);
app.post("", upsertProgress);

Choose a reason for hiding this comment

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

P1 Badge Expose history handlers on /list and /record

Both handlers are mounted at the history prefix root, so the backend only serves GET/POST /api/v1/history, but the current frontend and generated client call GET /api/v1/history/list and POST /api/v1/history/record (see frontend/src/routes/_layout/history.tsx and frontend/src/routes/_layout/read.tsx). This mismatch makes history fetch/save fail as soon as the Node backend is used.

Useful? React with 👍 / 👎.

Comment on lines 6 to 8
fs_roots: config.FS_ROOTS
? config.FS_ROOTS.split(",").map(r => r.trim()).filter(Boolean)
: [],

Choose a reason for hiding this comment

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

P1 Badge Return fs_roots in settings as a string

The settings response now serializes fs_roots as an array, but the existing UI expects a comma-separated string and immediately calls .split(",") (frontend/src/routes/_layout/settings.tsx), so a non-empty response will throw value.split is not a function and break settings rendering. Keeping wire format compatible avoids a client runtime error.

Useful? React with 👍 / 👎.

}

export async function settingsRoutes(app: FastifyInstance) {
app.get("", getSettings);

Choose a reason for hiding this comment

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

P1 Badge Register a PUT handler for /api/v1/settings

Only GET is registered for this route, but the settings page persists changes with PUT /api/v1/settings (frontend/src/routes/_layout/settings.tsx), so saving FS roots/favorite/already-read paths cannot work against this backend. This is a functional API regression for user settings updates.

Useful? React with 👍 / 👎.

Comment on lines +66 to +69
app.setNotFoundHandler((_req, reply) => {
const indexPath = path.join(frontendDist, "index.html");
if (fs.existsSync(indexPath)) {
return reply.type("text/html").send(fs.createReadStream(indexPath));

Choose a reason for hiding this comment

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

P2 Badge Bypass SPA index fallback for unmatched API routes

When frontend/dist exists, the global not-found handler serves index.html for every unknown path, including /api/..., so missing API endpoints return HTML with status 200 instead of a JSON 404. That causes API consumers to fail with parse/runtime errors and hides which endpoint is actually unsupported.

Useful? React with 👍 / 👎.

Comment on lines +11 to +12
export function getExt(filepath: string): string {
return path.extname(filepath).toLowerCase();

Choose a reason for hiding this comment

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

P2 Badge Detect .tar.gz archives before calling extname

getExt relies on path.extname, which only returns the final suffix (e.g. .gz for file.tar.gz), so ARCHIVE_SUFFIXES entry .tar.gz is never matched in getFileType. As a result .tar.gz files are misclassified as unknown, and archive-specific behavior is skipped for them.

Useful? React with 👍 / 👎.

@hjyssg
Copy link
Owner Author

hjyssg commented Feb 20, 2026

连续长时间写,ai会注意力丢失。
我叫他模仿python版本写。但它会偷偷写一个简化版。thumbnail的逻辑是简化的。

hjy added 23 commits February 21, 2026 02:06
**IO与后端性能:** `Promise.all()` 并行化目录 stat 操作;schema.sql 添加 13 个索引覆盖所有 WHERE/JOIN 字段;所有列表接口强制分页(带 max cap);文件服务全部使用 `createReadStream` 流式处理。

**代码质量与UT:** 新增 history、parse、recService、repository-boundary 四个测试文件,修复 fs.test.ts 的 5 个失败(改用 `fs.promises` 异步 mock),全部 12 个测试文件 230 个用例通过。

**对接与类型安全:** 前端广泛使用 `?.` 可选链;消除了 `settings.tsx` 和 `useFileNavigation.ts` 中的 `as any`,后端源码零 `any`;全局错误通过 `QueryCache/MutationCache` 的 `onError` + `showErrorToast` 实现透传。

额外:应用户要求,用 `p-limit` 为 thumbnail 生成(并发 3)和 archive 解压(并发 2)加入了 HDD 保护。
- `files` 表保留历史记录,通过 `is_missing` 标志位 + `last_seen_at` 时间戳区分"存在"和"已消失"
- `scanDirectory` 扫描每个目录后调用 `markMissingInFolder`,将磁盘上消失的文件标记为 `is_missing=1`,保留记录不删除
- 搜索端通过 `presence_filter` 控制结果范围:默认只返回存在的文件(`is_missing=0`),`all` 含历史记录,`watched` 只返回有观看进度的,`scanned_recent` 只返回 10 分钟内扫描到的
- 清理了所有废弃字段引用(`fingerprint`/`scan_state`)和已废弃的 `syncFileTable` 接口(前后端均已移除)
hjy added 28 commits February 21, 2026 22:14
- `start.bat` — 生产模式,build 前端 → 启动后端 → 自动打开浏览器
- `dev.bat` — 开发模式,弹出两个独立窗口分别跑 Vite HMR 和 tsx watch,打开 localhost:5173
- `.vscode/tasks.json` — VS Code 里 `Ctrl+Shift+B` 直接触发 Dev 模式

**GitHub Release 自动打包:**
- `.github/workflows/release.yml` — 推 `v*` tag 自动触发,产出免安装 zip

发布一个版本只需:
```bash
git tag v1.0.0
git push --tags
```

Actions 自动完成:build 前端 → 编译 TS → 下载 Node.js 22 便携版(仅 node.exe)→ 组装 zip → 创建 Release,附带中文安装说明。

用户拿到 zip 解压后,编辑 `.env` 填上漫画目录,双击 `ShiguReader.bat` 即可,零安装,无 EXE,无签名问题。
…B 取候选文件路径后按需生成缩略图并 stream 返回,无候选或生成失败时返回 404。前端核心逻辑集中在 `EntityCard`:`thumbnail` 有值用原路径,为 null 且有 `entityType` 时自动构造懒加载 URL,`ThumbnailImage` 的 `onError` 处理 404 降级为占位图标。三个页面各只加了一行 `.map()` 注入 `entityType`,无重复逻辑。
Task 2 — `POST /api/v1/search/quick-match-batch` 实现:解析 EH 标题 → author/group/title 中段关键词搜候选 → 噪音剥离+卷号识别+Levenshtein 打分 → 返回 `downloaded/likely/same_author/different`。`/api/search/quick-match-batch` 兼容别名已注册,与 Tampermonkey 脚本直接对接。

Task 3 — `progress` 表简化为 `read_history` append log,去掉所有进度字段,`history.ts` 改为 `GET /list` + `POST /record`。

Task 4 — `tests/integration/api.integration.test.ts` 用真实 SQLite 覆盖 search/history/quick-match-batch/authors/tags 全部主要接口,包含去重、分页、presence_filter、卷号区分等边界场景。

另修复了 titleMatcher 的 Levenshtein fallback bug(改用 baseA/baseB 避免卷号差异绕过 differentVolume 检测)。
1. Thumbnail 并发卡主进程 — 用户决定跳过
2. Archive 页面 view mode — 删除了 archive.tsx,改为 explorer 页面统一处理:点击压缩包直接跳转 `/explorer?archivePath=xxx`,explorer 内部用 `useArchiveExtract` hook 触发解压后重定向到解压目录。read-mobile、read-waterfall、video、audio 的返回链接全部更新,全局零残留 `/archive` 路由引用
3. path vs filePath — `filePath` 重命名为 `sourceFolderPath`,保留向后兼容(`|| search.filePath`)
…转,复用 FileViewContainer 三种视图;任务3将 filePath 重命名为 sourceFolderPath 并移除向后兼容。本次修复了 explorer.tsx 最后3处 navigate 缺少 archivePath: "" 的 TS 错误,构建通过。
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.

1 participant