
语言 🇨🇳 简体中文
Val Town 安装
为 Val Town 应用添加实时评论
Val Town 在 Deno 上运行 TypeScript,因此 val 是一个真实的服务器。这使它非常适合 FastComments:小部件是页面上的 script 标签,任何需要密钥的功能,如 Secure SSO 或验证 webhook,都可以在同一个 val 的服务器端运行。
本指南涵盖了将评论小部件添加到 HTTP val、在索引页面显示评论计数、使用用户已有的 Val Town 账户登录以及接收评论 webhook。
您无需账户即可尝试。示例使用 tenantId: "demo",这是一个共享的沙盒环境,第 2 步将介绍如何切换到您自己的。
步骤 1:添加小部件 
The widget is a script tag and a container element, so it drops into whatever your val already renders. This example uses Hono JSX, which is what Val Town's HTTP templates use.
Run 
在发布前选择 urlId
urlId 决定评论落入哪个线程。如果不设置,它会默认使用当前页面 URL 的清理版本,这正是 Val Town 上会变化的东西:一个 val 在你声明子域之前会有一个长的 *.web.val.run 主机名,分支会拥有各自的 URL,重命名页面会改变路径。每一种变化都会悄悄地生成一个独立的空线程,表现为“我的评论消失了”。
将其设置为你可控制的稳定值,例如上面的文章 slug 或数据库 ID。同时也传递 url,以便通知邮件和审核工具能够链接回真实页面。
在没有 JavaScript 的情况下保留评论
FastComments 在服务器端渲染完整的线程,val 可以将其放入 <noscript> 块中:
Run 
对参数进行 URL 编码。服务器端版本支持匿名和已登录的评论、单点登录(SSO)以及嵌套回复。
步骤 2:使用您自己的账户 
tenantId: "demo" 是一个共享的公共沙盒。它无需注册即可使用,这也是示例使用它的原因,但其他使用 FastComments 的人会写入相同的线程,任何人都可以对其进行审核。在发布任何重要内容之前请切换。
您的租户 ID 位于 API secret page。
租户 ID 是公开的,应该放在浏览器代码中。API 密钥不是公开的,且本页的内容不需要它。
从环境变量读取
Val Town 的 vals 在免费层是公开的,因此它们的源代码是全局可读的。请将任何敏感信息保存在环境变量中,并使用 Deno.env.get 读取:
Run 
这在 Val Town 上尤为重要,原因有二:重新混合一个 val 会复制环境变量的键,但不会复制其值。 保存在环境变量中的密钥不会随你的 val 进入他人的账户,而写入文件的密钥会。
回退到 "demo" 可以让在设置自己租户之前重新混合该 val 的任何人都能正常使用。
EU 账户
一个账户、其数据以及密钥都位于同一地区。如果您的账户是在 eu.fastcomments.com 创建的,则每个小部件配置也需要 region: "eu",并且脚本会从 cdn-eu.fastcomments.com 加载。否则保持默认即可。
索引页上的评论计数 
在索引页面上,不要为每一行渲染一个 comment-count 小部件。这会对每篇文章产生一次请求。使用批量计数,它只需要对整页发起一次请求。
为每一行标记其线程使用的 urlId,然后一次性加载批量小部件:
Run 
脚本会查找页面上所有 .fast-comments-count 元素并填充其计数。
data-fast-comments-url-id 必须与帖子评论小部件使用的 urlId 相匹配。如果小部件使用 slug,则标记也使用 slug。若不匹配,则会在已有评论的线程上显示为零。
脚本会轮询 window.FastCommentsBulkCountConfig,因此在脚本标签之前或之后设置配置都没有关系。
使用 std/oauth 的安全单点登录 
If your val already knows who the visitor is, Secure SSO hands that identity to the widget so they never see a second login. There are no endpoints to build and nothing to call at runtime: you compute three values server-side and pass them in the widget config.
Val Town ships zero-config login with std/oauth, so the visitor can sign in with the Val Town account they already have. Swap that for whatever your app uses; the FastComments half does not change.
在服务器端构建负载
The API secret signs the payload and must never reach browser code. Install the SDK from npm, which works on Val Town's Deno runtime as-is:
Run 
getPayload() returns { userDataJSONBase64, verificationHash, timestamp }. Those three values are all that reach the browser. The secret signs them and is then dropped, so nothing in the page lets a reader forge a different user.
将其传递给小部件
Run 
oauthMiddleware adds GET /auth/login, GET /auth/callback and POST /auth/logout for you. Note that logout is a POST, while the widget navigates to logoutURL with a GET, so point logoutURL at a small route of your own that submits the POST.
When the visitor is logged out, pass sso with only a loginURL. The widget then shows a login prompt instead of an anonymous comment box.
常见问题
timestamp is epoch milliseconds, must not be in the future, and must not be more than two days old. Generate it on the server in the same request that computes the hash. Generating it in the browser is the classic failure: the value differs from the one that was hashed and every comment is rejected.
Never set isAdmin or isModerator from the identity provider. Signing in with a Val Town account says nothing about who should moderate your site.
See the SSO guide for the full field list, group-gated threads, and badges.
接收 Webhook 
A val 是一种天然的 webhook 接收器:它拥有稳定的 URL,能够验证签名,并内置 SQLite 和 blob 存储。
FastComments signs ${timestamp}.${body} with your account's API secret and sends two headers:
Run 
The method carries the event: PUT for a created or updated comment, DELETE for a deleted one.
Run 
两个常见问题
验证原始字节。 解析 JSON 并重新序列化会改变键的顺序和空白字符,导致哈希不同,所有交付都会因未知原因失败。这是 webhook 接收器“根本不起作用”的常见原因。
常量时间比较。 对签名使用普通的 === 会泄露匹配的字节数,这足以一次伪造一个字节。
处理事件
快速响应。FastComments 会在非 2xx 响应时重试,且持续失败的端点会被自动禁用,因此应在响应后再进行实际工作,而不是在响应时内联处理。
使处理在评论 ID 上具备幂等性。重试时会使用新的时间戳重新签名,同一评论 ID 在编辑和删除时会再次到达,因此没有稳定的字段可用于去重。
示例 Vals 
Four public vals you can remix, each covering one piece of this guide.
Blog with comments (live) 是一个 Markdown 博客,每篇文章下都有一个线程,并在索引页显示批量评论计数。它在你 remix 的瞬间即可工作,只需一个环境变量即可指向你的账户。
SSO demo (live) 使用访客的 Val Town 账户登录,并将该身份传递给小部件,因此无需二次登录。
Webhook receiver (live) 对每次交付的 HMAC 签名进行验证,并将事件存储在 SQLite 中。它有一个按钮可以对测试负载进行签名并发送给自身,这样你可以在配置真实 webhook 之前看到验证成功。
Agent skills (live) 是一个 FastComments 代理技能库,涵盖小部件、SSO、REST API、审核以及从 Disqus 迁移。Remix 它后,Val Town 的代理 Townie 会自动从 skills/ 中加载这些技能,使你的代理能够在不需要你将文档粘贴到聊天中的情况下,了解如何接入评论。
The same skills install anywhere else with npx skills add fastcomments/skills.
域错误 
一旦关闭 demo 租户,小部件可能会因授权错误而拒绝加载。这是因为 FastComments 不知道它应该允许在该域上使用您的账户。
Val Town 在这里值得再次关注,因为一个 val 可以通过多个主机名访问:
- 每个 HTTP val 都有一个较长的默认端点,
-- .web.val.run。 - 声明自定义子域名会添加
.val.run。 - 自定义域名 会再添加一个。
- 分支会拥有自己的 URL。
添加您实际提供小部件服务的所有主机名。如果在设置完成后再声明子域名,请也将其添加,否则小部件将在旧 URL 上工作,而在新 URL 上失败。