{"version":"https://jsonfeed.org/version/1.1","title":"中文 — lepture","description":"关于编程、开源、阅读、旅行与日常生活的记录。","home_page_url":"https://lepture.com/zh/","feed_url":"https://lepture.com/zh/feed.json","icon":"https://lepture.com/avatar.jpg","language":"zh","items":[{"id":"https://lepture.com/zh/blog/building-prosefly","url":"https://lepture.com/zh/blog/building-prosefly","title":"开发 Prosefly","summary":"做过 Typlog，为何又做 Prosefly？谈谈起因、开发的顺序，以及选型时走过的弯路。","content_html":"<p>我已经做了一个博客和播客托管平台 <a href=\"/zh/projects/typlog\">Typlog</a>，最近又做起了 <a href=\"/zh/projects/prosefly\">Prosefly</a>。它可以管理 GitHub 仓库里的文档和文章，提供一个可视化编辑器，写完后保存的仍是 Markdown、MDX 文件。</p>\n<h2 id=\"为什么又做一个\">为什么又做一个</h2>\n<p>既然已经有了 <a href=\"https://typlog.com\">Typlog</a>，为何又要做一个 <a href=\"https://prosefly.com\">Prosefly</a> 呢？</p>\n<p>我以为现在是做静态网站的好时候。有了 AI，做一个网站容易多了，自己设计一个主题亦不再那么麻烦。文章保存成 Markdown、MDX 文件，和网站代码放在一起，AI 便可以直接读取、修改，改文章时也可以顺便改一下页面。</p>\n<p>不过，文章保存在哪里是一回事，怎样写文章又是另一回事。我仍然想要一个好用的写作后台。调整格式、插入图片，总不想每次都去修改源码。静态网站的自由我想要，写作时的方便亦不想舍弃，于是便有了 Prosefly。</p>\n<p>还有一个缘由，是文章的 frontmatter。在 Typlog 里，这些字段都是我预先定义好的，用户不方便改。假使想写书评，需要加上作者和评分，便要看平台有没有提供这些字段了。网站可以按自己的想法改，文章的字段却不能随意定义，多少有些不便。</p>\n<p>Prosefly 的做法是直接读取项目中用文本定义的 frontmatter schema，再生成相应的编辑界面。需要什么字段，便在 schema 里添加什么字段。这样我做一个新模板时，也不必先想平台支不支持了。</p>\n<h2 id=\"开发顺序\">开发顺序</h2>\n<p>最先做的是可视化编辑器，这一部分花了很久。当时还没有开始写完整的平台，只想着先把编辑器做好：写作时可以方便地调整格式，保存下来又是 Markdown、MDX 文件。说起来简单，做起来却颇费时间。</p>\n<figure class=\"pf-video-player\" data-astro-cid-r4k5drvp><pf-video-player class=\"not-prose\" data-short-video-seconds=\"30\" data-astro-cid-r4k5drvp=\"true\"><video src=\"https://media.prosefly.com/lepture/0b908530761a489bb3e17f2116775092.mp4\" controls playsinline preload=\"metadata\" aria-label=\"Prosefly 的可视化编辑器\"></video><div class=\"pf-video-player__actions\" data-pf-video-actions hidden data-astro-cid-r4k5drvp><button type=\"button\" class=\"pf-video-player__button\" data-pf-video-playback data-play-label=\"Play\" data-pause-label=\"Pause\" aria-label=\"Play\" title=\"Play\" data-astro-cid-r4k5drvp><svg class=\"pf-icon\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" data-pf-video-icon=\"play\" data-astro-cid-r4k5drvp=\"true\" aria-hidden=\"true\" focusable=\"false\" data-astro-cid-af3dxg6y><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><svg class=\"pf-icon\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" data-pf-video-icon=\"pause\" data-pf-video-hidden=\"true\" data-astro-cid-r4k5drvp=\"true\" aria-hidden=\"true\" focusable=\"false\" data-astro-cid-af3dxg6y><g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><rect width=\"5\" height=\"18\" x=\"14\" y=\"3\" rx=\"1\"/><rect width=\"5\" height=\"18\" x=\"5\" y=\"3\" rx=\"1\"/></g></svg></button><button type=\"button\" class=\"pf-video-player__button\" data-pf-video-sound data-mute-label=\"Mute\" data-unmute-label=\"Unmute\" aria-label=\"Unmute\" title=\"Unmute\" hidden data-astro-cid-r4k5drvp><svg class=\"pf-icon\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" data-pf-video-icon=\"muted\" data-astro-cid-r4k5drvp=\"true\" aria-hidden=\"true\" focusable=\"false\" data-astro-cid-af3dxg6y><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 4.702a.7.7 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.7.7 0 0 0 11 19.298zm5.5 9.798l5-5m-5 0l5 5\"/></svg><svg class=\"pf-icon\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" data-pf-video-icon=\"audible\" data-pf-video-hidden=\"true\" data-astro-cid-r4k5drvp=\"true\" aria-hidden=\"true\" focusable=\"false\" data-astro-cid-af3dxg6y><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298zM16 9a5 5 0 0 1 0 6m3.364 3.364a9 9 0 0 0 0-12.728\"/></svg></button></div></pf-video-player><figcaption>Prosefly 的可视化编辑器</figcaption></figure>\n<p>等编辑器接近完成，才开始做 Prosefly。不过亦没有立刻写 Web 应用，而是先创建了 GitHub 组织，做了几个 Astro 主题和模板，然后才开始开发平台。</p>\n<p>先做模板，是想看看实际的网站里，文章应该怎样组织，frontmatter 应该怎样定义，编辑器又该如何配合。只对着编辑器想，总不如拿一个网站来试试。于是等到开始写平台时，已经有了可以编辑的内容，也有了使用这些内容的网站。</p>\n<h2 id=\"犯过的错\">犯过的错</h2>\n<p>Prosefly 是用 Cloudflare Workers 写的，需要按组织拆分数据库，让各个组织的数据互不混杂。我最初选了 <a href=\"https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/\">Workers for Platforms</a>，它可以部署和运行用户的 Worker 代码，用来做数据隔离亦是可以的。</p>\n<p>可是项目越写越复杂。编辑器已经花了许多时间，模板也做了几个，到了平台这一层，却越做越麻烦，一度想就此放弃。原本只是想把各个组织的数据分开，结果还要部署和管理不同的 Worker，平白多出许多事来。</p>\n<p>后来才发现，<a href=\"https://developers.cloudflare.com/durable-objects/\">Durable Objects</a> 便可以做我需要的事。每个组织的数据放在各自的 Durable Object 里，应用仍是同一个应用，也不必为此部署和管理不同的 Worker。换了方案后，平台简单了许多，才又有心思继续做下去。</p>\n<p>现在想来，完全是自己想复杂了，倒险些把项目也一并放弃了。</p>","date_published":"2026-10-11T00:00:00.000Z","language":"zh","tags":["Prosefly"]}]}