发布记录

发布记录

写写贴 (wpaste) 的产品发布历程。每条目按发布时间倒序排列。

说明:由于早期提交历史被 squash 成单个 commit,初期版本的具体时间线未能完整保留。本文档从当前可还原的里程碑开始记录。


🆕 2026-04-11(下午)· 镜像体系完善 + 迁移链修复

在上午交付的镜像 MVP 基础上,补齐了四件事:长文档分段翻译、增量翻译缓存、术语表、套餐配额。HTML 站点镜像暂不做,其余全部上线。

1. 长文档分段翻译 + 增量翻译缓存

核心改动:lib/mirror.ts 的 translateMarkdown 从"一次性 LLM 调用"改成"按 H2 边界切分的块级流水线"。

分块策略 (splitIntoBlocks):

  • 按 ^## 切分文档;跳过 fenced code block 内的 ## 避免误判
  • Block 0 = 文档开头到第一个 H2(frontmatter + H1 + 引言)
  • Block 1..N = 每个 H2 到下一个 H2
  • 没有 H2 的短文档 → 单块路径(行为与之前一致)

增量缓存 (document_mirrors.block_cache):

  • 新列,JSON 格式 { sha256(blockContent): translatedBlock }
  • 每次 regenerateMirror 前加载历史缓存
  • 每个块:命中缓存 → 0 LLM 调用直接复用;未命中 → 翻译并写入新缓存
  • 翻译完成后把新缓存持久化回 block_cache 列

效果:

  • 长文档(多 H2)天然被切成多次小 LLM 调用,不再受上下文上限约束
  • 完全未改的源文档重译 → 0 次 LLM 调用
  • 只改一个段落 → 只有那一个块被重译,其他块复用
  • wpaste mirror <id> --lang en --regenerate(force: true)清空缓存读,强制从头重译

2. 术语表 (Glossary)

数据模型:新表 user_glossaries,唯一索引 (user_id, source_term, language)。每用户每目标语种维护一份术语表。

REST API /api/v1/glossary:

  • GET [?language=en] — 列出(可按目标语种过滤)
  • POST { sourceTerm, translation, language } — upsert:已存在 → 200 updated: true;新建 → 201 created: true
  • DELETE ?id=...

CLI:

bash
wpaste glossary list [--lang zh|en|ja]
wpaste glossary add "写写贴" "wpaste" --lang en
wpaste glossary remove <id>

Prompt 注入:regenerateMirror 自动 loadGlossary(userId, language) 读出用户在目标语种下的所有术语,追加到翻译 system prompt 的 Glossary — you MUST use these exact translations ... 段落里。

注意:术语表只对新翻译的块生效,历史缓存不会被自动 re-apply。想让新术语完整生效:跑一次 wpaste mirror <id> --lang en --regenerate。

3. 套餐配额 (Plan Quotas)

在 lib/plan-limits.ts 加两项限额:

PlanmaxMirrorsmaxGlossaryEntries
free510
pro500200
team50002000

执行点:

  • POST /api/v1/documents/:id/mirrors — 仅在新建时检查;超限返回 403 + code: "LIMIT_MIRRORS" + upgrade_url
  • POST /api/v1/glossary — 同理,upsert 更新免配额,只有 created=true 的 insert 触发 LIMIT_GLOSSARY
  • 幂等行为:已有 (source, language) 镜像或 (term, language) 术语再 POST 不消耗额度

4. 迁移链修复

背景:fresh local SQLite init 一直是坏的 —— 0000 → 0002 的迁移链里丢了一个加 documents.tags 的迁移,导致 0002 的 INSERT INTO __new_documents ... SELECT tags FROM documents 在新库上炸 no such column: tags。

修复:scripts/init-db.ts 从"按序 replay 所有 migration SQL"改成 shell out 到 drizzle-kit push,绕过坏掉的链直接从 db/schema.ts 同步 schema。drizzle.config.ts 加 dbCredentials.url 读 DATABASE_PATH 环境变量,让 self-hoster 能指定自定义路径。

Cloudflare D1 不受影响:线上 D1 早在 bug 出现之前就 bootstrap 过了,仍然走 wrangler d1 migrations apply。今天顺手把 0014 / 0015 / 0016 一起 apply 到了 production D1。

5. CLI Marp 误判修复

症状:今天上午发"产品功能总览"到 wpaste 时,CLI 把它误判成 Marp 幻灯片(docType: "slides")。触发条件是文档里字面包含了 marp: true(作为功能说明)+ 文档有多个 --- 水平线分隔。

根因:cli/src/commands/publish.ts 的检测正则 /^---[\s\S]*?marp:\s*true[\s\S]*?---/m 因为 [\s\S]*? 跨越 frontmatter 边界,把"正文提到 marp: true + 后面有 ---"的任何文档都判成幻灯片。

修复:抽成独立纯函数 detectMarpSlides(content),先严格匹配文档开头的 ^---\s* ([\s\S]*?) ---,再在提取出的 frontmatter 块内用 ^\s*marp:\s*true\s*$ 行匹配。锁 14 个回归测试(包括今天真实触发的那种文档)。

6. 测试体系

  • 主项目:从 202 → 241 个测试(+35 个新增,覆盖 splitIntoBlocks、缓存行为、glossary CRUD、配额)
  • CLI:独立 vitest 配置 + 14 个 detectMarpSlides 回归测试
  • 总计:255 个测试,0.5 秒跑完 CLI,2 秒跑完主项目
  • 新增 tests/api/glossary.test.ts(18 个 HTTP 层测试)
  • 覆盖率(在 lib/ + 镜像路由):~45% lines,镜像相关代码 >95%

7. 文档更新

  • docs/mirror.md — 新增"分块翻译与增量缓存"、"术语表"、"配额与计费"三大段;"未来方向"缩到只剩 HTML 镜像和若干 nice-to-have
  • docs/api.md — 新增 /api/v1/glossary 段落 + LIMIT_MIRRORS / LIMIT_GLOSSARY 错误码
  • docs/cli.md — AI 能力表追加 wpaste mirror 和 wpaste glossary
  • docs/database.md — 记录迁移链修复的历史和 workflow 变化

本次交付的 Git Commit

32d820d feat(db): mirror block cache, user glossary table, plan quotas
8bec54b feat(mirror): block-aware translation with incremental cache + glossary
576088f feat(api): enforce mirror plan quota on create endpoint
eea76f4 feat(glossary): REST API + wpaste glossary CLI
1b07e84 docs: update mirror / api / cli for chunking, cache, glossary, quotas
1540a1a fix(db): bypass broken migration chain in init-db.ts

🆕 2026-04-11(上午)· 镜像功能 + 技术 Wiki + 测试基础设施

镜像(Mirror)— 自动外语翻译

给知识库加了"镜像"能力:一份文档可以自动生成其他语言的副本,作为关联的只读文档保留在同一个 workspace 里。

交付内容:

  • 支持 中 / 英 / 日 三种目标语言
  • 新增 document_mirrors 关联表 + documents.language 列
  • 4 个新 API 端点:
    • POST /api/v1/documents/:id/mirrors — 创建镜像(首次同步翻译)
    • GET /api/v1/documents/:id/mirrors — 列出已有镜像
    • POST /api/v1/documents/:id/mirrors/:lang — 强制重生
    • DELETE /api/v1/documents/:id/mirrors/:lang
  • CLI 新命令:wpaste mirror <id> --lang en [--list|--regenerate|--delete]
  • 阅读页新增 语言切换器 组件(pill 样式,按主题配色)
  • Wiki-link 跨语言解析:镜像里的 *Title* 自动链到同语言的其他镜像(有则指向镜像,无则回退到原文)

核心行为:

  • 首次创建阻塞翻译:POST 请求等翻译完成才返回,调用方拿到的 URL 是可直接访问的
  • 源文档更新后自动重生:PATCH 源 → 标记所有镜像 stale → waitUntil 后台重译
  • 镜像只读:PATCH 到 doc_type=mirror 一律 400,防止改动被下次重译覆盖
  • 代码块保护:fenced 和 inline code 在发送给 LLM 前替换为 {{CODE_N}} 占位符,不参与翻译
  • H1 自动同步:翻译后提取第一个 # 更新 documents.title
  • 删除级联:删除源文档时先删所有镜像 doc,再删源,document_mirrors 关联行靠 FK CASCADE 清理
  • 过滤 title 冲突:POST /api/v1/documents 的按 title 幂等覆盖逻辑排除 doc_type=mirror,避免误覆盖镜像
  • doc_type 限制:raw / wiki / index / answer 可镜像,html / slides 不行

数据迁移:db/migrations/0015_noisy_korg.sql


技术 Wiki(docs/)

新增 13 篇技术文档,从入门到参考完整覆盖:

文档内容
docs/README.md索引 + 技术栈速览 + 阅读顺序建议
docs/architecture.md系统总体架构、双形态部署拓扑、核心抽象
docs/database.mdDrizzle schema、表间关系、迁移流程
docs/api.md全部 REST API 参考(含 /api/v1/* 和 /api/auth/*)
docs/auth.mdGitHub OAuth + Bearer Token 双重认证细节
docs/ai.md多后端 AI Provider 抽象 + Job 流水线模式
docs/mirror.md镜像功能完整设计文档
docs/storage.mdR2 / 本地 FS / S3 存储抽象
docs/cli.mdwpaste CLI 命令参考与架构
docs/themes.md18 套主题 + 自定义主题系统
docs/workers.mdmarp / hn-scraper / static 三个辅助 Worker
docs/deployment.mdCloudflare 边缘部署 + Docker 自托管
docs/development.md本地开发、常见坑、调试技巧

测试基础设施

从 0 到 202 个测试、13 个测试文件:

类别文件数测试数
纯函数单元测试8154
集成测试(内存 SQLite)226
API 路由测试338
总计13202

覆盖率(在 lib/ + 被测 API 路由范围):

  • 镜像核心 lib/mirror.ts:95.65% lines
  • 两个镜像路由文件:100% lines
  • lib/wiki-links.ts / encrypt.ts / frontmatter.ts / mirror-prompts.ts / plan-limits.ts / toc.ts:100%
  • lib/custom-theme.ts:93.54%
  • 总体加权:45.08% lines

技术选型:

  • vitest v4 + @vitest/coverage-v8
  • 集成测试用 better-sqlite3(':memory:') + 提交的 tests/schema.sql 快照
  • 测试 harness:makeTestDb() / seedUser() / seedDocument() / callRoute() / stubProvider() / failingProvider()
  • API 路由测试用 vi.hoisted + vi.mock 注入 fake DB / auth / AI provider

生产代码的一处小改动:lib/mirror.ts 的 regenerateMirror() 增加可选 opts.provider?: AIProvider 参数,支持依赖注入以便测试。生产路径继续走 getAIProvider 工厂,行为不变。

新增脚本:

bash
npm test              # 一次性跑
npm run test:watch    # 开发时 watch
npm run test:coverage # v8 覆盖率报告

🚀 初始上线(0f015a5 "feat: initial commit")

一次性交付了一个完整的 markdown 文档托管平台。此前的 commit 87d9387 是 create-next-app 的脚手架。

基础设施

  • Next.js 16 App Router + React 19 + TypeScript 5
  • 双形态部署:Cloudflare Workers(@opennextjs/cloudflare)+ Docker/VPS(standalone)
  • 数据库双态:Cloudflare D1 / 本地 better-sqlite3,通过 Drizzle ORM 共享 schema
  • 存储抽象:R2 / 本地文件系统 / S3(简化版),可切换
  • AI Provider 抽象:Workers AI / OpenAI / Anthropic / Ollama 四种后端

核心功能

  • Markdown 文档发布、读取、更新、删除
  • 全文搜索(LIKE 模糊匹配,含标题 + 内容)
  • Workspace / Folder 层级组织
  • Wiki-link *Title* 语法 + 反向链接
  • 文档片段(Data Fragments)交互组件
  • HTML 静态站点托管(多文件 → R2)
  • Marp 幻灯片支持(独立 Worker 即时渲染)

18 套主题

  • 8 套通用:github / academic / night-owl / ink / terminal / notion / craft / editorial
  • 10 套国家主题:china / japan / korea / france / britain / germany / italy / usa / brazil / india
  • Pro+ 支持自定义主题(JSON 配置 + 可选的 AI 生成)

认证体系

  • GitHub OAuth(arctic + 自实现 HMAC session)
  • API Token 体系(wpaste_ 前缀,SHA-256 存库)
  • 双认证中间件(Bearer 优先,Session 兜底)
  • 套餐体系:free / pro / team,过期自动降级

AI 能力

  • compile:原始笔记 → 结构化 wiki 文章 + 索引页 + 交叉引用
  • organize:未整理文档 → AI 归类提案
  • ask:RAG 问答,带 *引用*
  • lint:知识库健康检查
  • context:知识库摘要导出
  • BYOK:用户自带 API key(AES-256-GCM 加密)
  • 按 plan 分级模型(free 小 / pro+ 大)
  • 异步 Job 流水线(客户端轮询 + 分步 process,绕开 Workers CPU 时限)
  • 结果缓存:document_summaries 按 content_hash 跳过重复调用

CLI (wpaste)

独立 npm 包,commander + ink TUI:

  • 认证:auth / whoami
  • 文档 CRUD:publish / list / view / open / update / append / prepend / delete / move
  • 标签:tag add/remove/list
  • 组织:workspace create/list/delete / folder create/list/delete
  • 搜索:search
  • AI:organize / compile / ask / lint / context
  • 其他:image / daily / ui / install-skills
  • 支持 stdin 输入、目录输入(HTML 站点)、JSON 输出

Claude Code 集成

  • 7 个 skill:publish / save / search / ask / compile / context / daily
  • 通过 wpaste install-skills 自动安装到 ~/.claude/plugins/

社交 / 通知

  • 用户主页 /u/{username} + 子域名 {username}.wpaste.io
  • 关注 / 粉丝体系
  • 公共文档评论
  • Web Push(VAPID)+ 站内通知中心
  • Workspace 订阅(付费知识库)

辅助 Workers

  • marp.wpaste.io — Marp 幻灯片即时渲染(带 CF 边缘缓存)
  • static.wpaste.io — 静态站点回源
  • hn-scraper — HN TOP 20 每小时定时抓取 + AI 总结,自动发布

浏览器扩展

extension/ 目录下的 Chrome 扩展:网页内容 → turndown.js → wpaste 文档

自托管

  • docker-compose.yml + Dockerfile
  • scripts/init-db.ts 初始化本地 SQLite
  • 完整的 .env.example

📋 累计变更统计(2026-04-11 全日)

指标数量
新增数据库表2(document_mirrors、user_glossaries)
新增数据库列2(documents.language、document_mirrors.block_cache)
新增 API 路由3(/mirrors 集合、/mirrors/[language]、/glossary)
修改 API 路由2(documents/route.ts、documents/[id]/route.ts)
新增 lib/ 模块2(mirror.ts、mirror-prompts.ts)
新增 React 组件1(language-switcher.tsx)
修改页面1(app/d/[slug]/page.tsx,语言切换器 + 跨语言 wiki-link)
新增 CLI 命令2(wpaste mirror、wpaste glossary)
新增 Wiki 文档13(docs/*.md)
新增测试文件15
新增测试数量255(241 主项目 + 14 CLI)
新增生产代码(估算)~2,500 行
应用的 D1 迁移3(0014、0015、0016)

🔮 已知未做 / 未来方向

  • HTML 站点镜像 —— 静态站点(doc_type='html')的 lang="en" 副本;需要处理多文件、外链、JS 字符串等,工程量大
  • 正则术语表 —— 术语表目前只做字面匹配,未来可加 {regex} 匹配词形变化
  • Glossary workspace 共享 —— 团队共用术语表
  • 翻译评审界面 —— dashboard 里 diff 新旧译文并选择采纳

Comments (0)

No comments yet

imfht
April 10, 2026 · Updated April 11, 2026