@cloudhal/评论
对Cloudhal网站的第一读者评论RFC-0007 (英语), (中文(简体) ), ADR-0022 (英语)) (中文(简体) ). 特邀评论为节制,在D1直播 站点外图,被服务器送入缓存文章页面并被标签清除(cloudhal-comments-<nodeId>在自由清洗预算范围内。 Bots支付时附有工作第一人证;Turnstile是选入.
这里的一切 工人安全: 任何条目均不只导入节点。 存储和返回的记录是 Comment 合同合同 @cloudhal/core-contracts; 没有 API 返回电子邮件地址、 IP 散列或外部的挑战 commenter 访问器(invariant C1).
| 条目 | 什麽 |
|---|---|
@cloudhal/comments | 设置模式和 threadState、规范化、内容规则、链接、工作证明、提交/报告/质疑管道及其答案、钥匙和散列、隔离限制、清洗规划员、保留计划。 不依赖存储 . |
@cloudhal/comments/sql | SqlCommentStore 结束 SqlDriver (SQLite 本地和节点,D1 Cloudflare)用于迁移 0016_comments。 。 。 . |
@cloudhal/comments/client | 浏览器来源( 只有节点构建输入) : 注释模块 (/_cloudhal/c.<hash>.js, 4 KB gzipped)及其工作证明工人(/_cloudhal/cw.<hash>.js,纯-JS SHA-256, * * 1 KB),由该建筑公司建造 cloudhal() 整合;终点名称和预算. |
@cloudhal/comments/testing | describeCommentStoreConformance, (中文(简体) ), describeCommentPipelineSuite, (中文(简体) ), countingDriver, (中文(简体) ), expectContractComment (vitest) (英语). |
@cloudhal/comments/fixtures/comment-text.json | 共同的链接规则定律parseCommentText 用于渲染器和工作室的块. |
设置和线程状态(§4.5)
import { applySettingsInput, defaultCommentSettings, threadClosesAt, threadState } from "@cloudhal/comments";
const settings = await store.settings(siteId); // the row, or the defaults (off, rev 0)
threadState({ settings, entry: { nodeId, collection: "posts", publishedAt }, thread, hasApproved, now });
// → "open" | "closed" | "hidden" | "off" (90 days after first publication by default)
CommentSettingsInput 由 applySettingsInput (拒收未知密钥; CommentSettingsError代码 invalid_settings) (中文(简体) ). collectionEnabled, (中文(简体) ), closeAfterDaysFor, (中文(简体) ), threadVisible 财务报告和审定财务报表 DEFAULT_RETENTION 也输出.
案文(第6.4节,第8.1节)
normalizeText, (中文(简体) ),normalizeName, (中文(简体) ),normalizeEmail, (中文(简体) ),checkText, (中文(简体) ),checkName, (中文(简体) ),checkEmail, (中文(简体) ),codePoints, (中文(简体) ),cutCodePoints, (中文(简体) ),CONTRACT_FORBIDDEN——NFC,CRLF → LF, Control and bidi字符被去除(合同拒绝),代码分出长度.evaluateContent({ name, text }, { linkLimit, watchWords, blockedWords })→{ ok, status: "pending" | "spam", labels }或者说{ ok: false, field, problem }· ;compileWordList, (中文(简体) ),matchesWordList, (中文(简体) ),wordTokens, (中文(简体) ),foldForMatch(NFKC,土耳其语)İ编号 :I编号 :ı叠成一团,countLinks, (中文(简体) ),instructionLikeRules编号 :isInstructionLike(单位:千美元)INSTRUCTION_RULES_VERSION页:1,ruleLabels。 。 。 .- 链接 :
parseCommentText(text)页:1{kind:"text"}|{kind:"link", href}段;safeCommentHref, (中文(简体) ),linkifyCommentLine, (中文(简体) ),linkify· ;commentTextHtml(text)用于服务器交付的 HTML(已退出,<p>编号 :<br>, (中文(简体) ),rel="ugc nofollow noopener noreferrer"=:COMMENT_LINK_REL) (中文(简体) ). 仅https:有被点化的主机且没有用户信息成为链接的 URL 。 规矩相符@cloudhal/ui临时comment-model.ts; 固定针接住他们.
工作证明(第7条)
import { deriveCommentKeys, difficultyFor, issueChallenge, verifyChallenge, ReplayCache } from "@cloudhal/comments";
const keys = await deriveCommentKeys(env.CLOUDHAL_COMMENTS_KEY); // HKDF per purpose: pow, emailHmac, email, secrets
const { challenge, bits, expiresAt } = await issueChallenge(keys.pow, { siteId, target: nodeId, kind: "comment", bits: difficultyFor(today, cap), now });
v1.<payload>.<mac>无国籍者 10分钟 3秒 满分 18分升到20分21. solveChallenge 是参考解析器(测试,浏览器工人的算法). 单一用途: ReplayCache 每个隔离加唯一索引 challenge_id。 。 。 .
输油管(第6.2-6.6节)
import { handleCommentSubmission, handleReportSubmission, handleChallengeRequest,
submissionResponse, challengeResponse, CommentLimiter, DailyCount, ReplayCache, memoize } from "@cloudhal/comments";
import { SqlCommentStore } from "@cloudhal/comments/sql";
// once per isolate
const limiter = new CommentLimiter(), daily = new DailyCount(), replay = new ReplayCache();
const settings = memoize(() => store.settings(env.CLOUDHAL_SITE_ID)); // 60 s
const deps: PipelineDeps = {
siteId: env.CLOUDHAL_SITE_ID, available: true, hosts, clientAddress: request.headers.get("cf-connecting-ip")!,
keys, now: () => new Date(), settings, resolveEntry, store, limiter, daily, replay,
rateLimit?: (key) => …, // optional Rate Limiting binding
turnstile?: ({ token, remoteip, idempotencyKey }) => siteverify(…), // when the site uses Turnstile
};
return submissionResponse(await handleCommentSubmission(request, deps));
// reports: submissionResponse(await handleReportSubmission(request, deps), { pendingFragment: "comment-reported" })
// challenges: challengeResponse(await handleChallengeRequest(request, deps))
resolveEntry(nodeId) 返回时 已出版 条目({ nodeId, collection, path, publishedAt })或(或) null。结果 { kind: "pending", stored, dropped? } (蜂窝、糟糕的证明、重放、重复和禁止与存储的评论无法区分)或 { kind: "error", status, error, fields?, retryAfter? }; 表格状态 303 改为 #comment-pending, (中文(简体) ), #comment-invalid, (中文(简体) ), #comment-closed, (中文(简体) ), #comment-script-required, (中文(简体) ), #comment-slow-down, (中文(简体) ), #comment-try-again, (中文(简体) ), #comment-paused。在步骤12之前,没有任何文字;在步骤12的批次内插入一个月的第一个IP盐.
储存@cloudhal/comments/sql(中文(简体) )
new SqlCommentStore(sql, { now? })数字 :
| 区域 | 方法 |
|---|---|
| 设置、秘密 | settings, (中文(简体) ), putSettings(siteId, input, rev, by), (中文(简体) ), putSecret, (中文(简体) ), readSecret, (中文(简体) ), secretInfo, (中文(简体) ), deleteSecret |
| 线索 | thread, (中文(简体) ), putThread(siteId, nodeId, { mode, closesAt? }, rev, by, { purge?, audit? }) |
管道( R)SubmissionStore(中文(简体) ) | submit, (中文(简体) ), report, (中文(简体) ), approvedComment, (中文(简体) ), readSalt, (中文(简体) ), clientActivity, (中文(简体) ), isDuplicate, (中文(简体) ), storedSince, (中文(简体) ), pendingOnEntry, (中文(简体) ), isBanned, (中文(简体) ), reportActivity |
| 读取 | get, (中文(简体) ), list(siteId, { status | "reported", nodeId, nodeIds, label, since, cursor, limit ≤ 50, order }), (中文(简体) ), threadComments, (中文(简体) ), renderPage(siteId, nodeId, page), (中文(简体) ), counts, (中文(简体) ), history, (中文(简体) ), stateHash(siteId, ids, threads) (单位:千美元)cm1:(中文(简体) ) |
| 调和 | moderate(siteId, { ids ≤ 50, action, actor, reasonCode?, note?, expectedRevs, approvalId?, ownNodeIds?, audit? }) · 每项评论结果; label(siteId, { items, actor, source, expectedRevs?, ownNodeIds?, audit? })· ; reply(siteId, StaffReply)· ; editReply |
| 只有一个人 | findCommenter, (中文(简体) ), commenter(siteId, id, keys) (解密电子邮件), commenters(siteId, ids, keys) (三个读任何数字), commenterComments(siteId, id) (一读), hideApprovedOf(siteId, commentId, { actor, reasonCode }) (范围, more, (中文(简体) ), erase(siteId, { commenterId } | { commentIds }, "anonymise" | "delete-text", person, { note?, audit? }), (中文(简体) ), ban, (中文(简体) ), listBans(siteId, { commentId? }) (评论员禁止发表评论之一), liftBan |
| 报告 | listReports, (中文(简体) ), resolveReport |
purgeQueue | queued, (中文(简体) ), sites, (中文(简体) ), enqueue, (中文(简体) ), recent, (中文(简体) ), log (出版路径的清洗日志行), sent, (中文(简体) ), failed ——饲料 planPurge |
retention | run(siteId, retentionPlan(settings.retention, now)), (中文(简体) ), deleteEntries(siteId, deletedEntriesDue(…)), (中文(简体) ), runDeployment(now, oldestSaltMonthToKeep(now)) |
approve, (中文(简体) ), restore, (中文(简体) ), delete答复、消除和禁止需要一个人类行为者(CommentForbiddenError代码 forbidden); stale (中文(简体) ) rev这是 CommentConflictError (单位:千美元)conflict)用于单笔写作和评论 conflict 批量写作的结果. audit 声明附在同一批,最后一批。 每条路径的报表计数在店面上,并由合规套件申明.
清洗计划和保留(第8.4节,第9.3节)
planPurge({ rows, recent, now }) → { requests, nextCheckAt, takedownWaiting }:一次性取出(通常的标记会沿去取出),20 s后正常的标记,而最后一分钟显示的清除请求还不到3个(包括了),100个标记会请求. purgeBackoffMs, (中文(简体) ), settingsPurgeTags (或者300个条目以上的"一切"), commentsCacheTag, (中文(简体) ), PURGE_LIMITS。 。 。 . retentionPlan 列出受约束的步数(每跑500行).