← 返回文章流

告别不蒜子,自己给博客记个数

不蒜子好像又挂掉了。 之前已经遇到过几次。网页能打开,文章能看,偏偏浏览量那里空着。有时候浏览器还转很久,检查请求,才发现统计接口没有正常返回。以前看到过 504 Gateway Timeout,这次又得等。 本来想,免费的东西,等等也没什么。但一会儿有数字,一会儿没有,总归有点难受。于是又问 Codex:能不能用 GitHub、Cloudflare,或者办公室的Linux,自己记录一下?ai agent 我是离不开了。 不过有个前提:不能一换服务,之前的数字就全变成零。虽然也不是什么惊人的访问量,但好歹积累了这么久啊。 最后折腾成了下面这个样子AI帮忙折腾的,我自己估计得折腾很久。记一下,省得下次再问一遍。 网页还在 GitHub,计数交给 Cloudflare 博客仍然是Markdown写文章,Python生成网页,再把静态网站部署到GitHub Pages。 另外加一个 Cloudflare Worker 接收访问请求,用D1数据库保存计数。大概是这样: flowchart TD A[读者打开博客] --> B[GitHub Pages 提供网页] B --> C[浏览器加载计数脚本] C --> D[Cloudflare Worker 校验请求] D --> E[D1 保存访问记录并累计] E --> F[返回数字并显示在页面上] 这里的 Worker 不是再转头去问不蒜子,而是操作自己的数据库。否则只是多绕了一层,原来的服务不返回,最后还是没有数字。 没有把主服务放在办公室的 Fedora 上,是因为它也有自己的脾气。办公室可能停电,网络也可能断,电脑还得重启。之前图片服务就用了这台机器,不想再让访问统计也跟着它一起上下班。 这也不代表换到 Cloudflare 就永远不会出问题。只是现在计数保存在哪里、如何备份,我至少能自己查了。 先把授权弄明白 管理 Worker 和 D1 用的是 Cloudflare 的官方工具 Wrangler。这次使用的是 4.154.0。下面的命令都在博客项目根目录运行,默认已经有 Node.js 和 npm。 一开始,授权页面显示成功了,本机却没有拿到凭据。还有一次浏览器跳到 localhost:8976,直接提示: This site can't be reached localhost refused to connect ERR_CONNECTION_REFUSED 这个地址是登录过程临时使用的回调地址,不是博客挂了。但只看浏览器上的“授权成功”也不够,还得确认命令行真的接到了。 后来换成官方的设备授权方式,不再依赖浏览器访问本机回调端口: npx wrangler@4.154.0 login --device --use-keyring \ --scopes account:read user:read workers_scripts:write \ workers_routes:write d1:write zone:read 终端会给出设备码和授权页面。在浏览器里核对设备码,确认需要的权限,再点击 Authorize。等待中的命令不要提前关掉;这次也确实遇到过授权页报错,不能保证换一种方式就一次成功。 最后终端返回 Successfully logged in.,再检查: npx wrangler@4.154.0 whoami 确认账号和权限都对,这才算连上了。--use-keyring 用于把凭据以加密文件配合系统钥匙串保存,不把登录密钥写进博客仓库。设备码、密钥和完整授权回调地址,也不需要贴进文章里。 数据库、脚本和部署配置 这次新建了独立的 Worker 和 D1 数据库,都叫 seisamuse-counter-d1。没有覆盖原来的旧 Worker。 相关文件各有分工: 文件 用途 scripts/article_counter_20261028.js Worker 的请求处理和计数逻辑 scripts/article_counter_schema_20261028.sql 数据表和触发器 scripts/article_counter_manage_20261028.py 本地初始化、历史基数导入和备份工具 scripts/article_counter_worker_20261028.toml 云端绑定与部署配置 content/scripts/article_counter_20261028.js 浏览器端发送请求、显示数字 data/counter_20261028/history.json 手动确认的历史估算参数 这些是本次配套写好的脚本,下面的命令是在已有它们的前提下记录配置,不是只复制一个 TOML 文件就能得到计数器。 下面创建数据库和导入初始数据的操作,只用于第一次配置的空数据库。现在的数据库已经在计数,以后维护不要整段重新执行。 先创建数据库: npx wrangler@4.154.0 d1 create seisamuse-counter-d1 \ --config scripts/article_counter_worker_20261028.toml 把返回的数据库 ID 填进 TOML。公开记录里用占位符代替实际 ID,主要配置如下: name = "seisamuse-counter-d1" account_id = "替换为自己的账号ID" main = "article_counter_20261028.js" compatibility_date = "2026-10-28" workers_dev = false [vars] COLLECTION_ENABLED = "false" [[d1_databases]] binding = "DB" database_name = "seisamuse-counter-d1" database_id = "替换为新建数据库的ID" [[ratelimits]] name = "LIMITER" namespace_id = "20261028" simple = { limit = 300, period = 60 } [observability] enabled = false [[routes]] pattern = "stats.seis-jun.xyz" custom_domain = true main 相对于这个配置文件所在的目录;DB 和 LIMITER 则对应脚本使用的绑定。域名也要换成自己账号中可用的域名,不要照抄我的地址。 这里先把采集关着。数据库还没准备好,就别着急往里面写。 初始化时,先从生成的 site/ 整理页面路径,在本地创建数据库,再导入手动确认的历史基数。管理工具的处理顺序是: init → import-history(先做完整备份)→ export-seed 初始化时间需要明确指定,历史参数文件中的时间也要与它一致。这次保存的是 2026-10-28T09:18:11.000Z,即北京时间 17:18:11。它是新库的初始化边界,不是从原统计服务导出的时间。 export-seed 生成 SQL 后,再导入刚创建的空 D1: npx wrangler@4.154.0 d1 execute seisamuse-counter-d1 --remote \ --config scripts/article_counter_worker_20261028.toml \ --file data/counter_20261028/initial-seed.sql 本地 .sqlite 文件不是直接上传就能用,D1 导入需要 SQL;这点也可以查官方的导入导出说明。初始 SQL 不是日常发布文件,更不能拿它反复初始化正在使用的库。 然后部署 Worker: npx wrangler@4.154.0 deploy \ --config scripts/article_counter_worker_20261028.toml 先在采集关闭的状态下检查绑定和历史基数,确认没问题,再把 COLLECTION_ENABLED 改为 "true",重新部署。这样不至于一边测试,一边把测试访问也算进去。 网页怎么接上,怎么检查 浏览器端的入口放在公共模板 templates/base.html 中,所以文章页和其他页面不需要各自抄一遍: <script defer src="{{ root }}scripts/article_counter_20261028.js?v=2" data-counter-endpoint="https://stats.seis-jun.xyz/count"></script> ?v=2 是这次修改显示样式后加的版本标记,避免浏览器还沿用旧脚本。这个页面不再加载旧计数入口,也不在新接口失败后偷偷回退去请求原服务。 接口分成三种,不要拿错了: POST /count:正式网页提交一次浏览,写入计数。 GET /views?path=/:只查询某个路径的统计,不增加计数。 GET /health:检查服务绑定和采集开关,不查询数据库里的数值。 平时排查可以先用这两个只读请求: curl --max-time 15 'https://stats.seis-jun.xyz/health' curl --max-time 15 'https://stats.seis-jun.xyz/views?path=/' health 返回正常,并不等于数据库查询一定正常,还要看 views。第一次导入后,实际核对到历史浏览量是 80500、历史访客数是 40500,新增计数都是零,再开启采集。 本地 localhost 或 127.0.0.1 预览只读取统计,不提交浏览。否则我自己改个排版、刷新几十次,最后还以为突然来了不少读者。 正式发布一篇新文章时,Worker 会在首次计数前检查它是否确实是正式站点上能访问的 HTML 页面,成功后再登记。以后正常写文章,不需要每次手动把地址填进数据库。 旧数据没有想象中那么好搬 这次最纠结的其实不是部署,是旧数字怎么办。 我记得前几天总浏览量有八万零几百,访客数有四万零几百。最后确认,以 80,500 次浏览、40,500 位访客 作为历史起点。 但这两个数是根据回忆确认的估算,不是从不蒜子后台完整导出的精确记录。新系统把历史基数和新增记录分开保存,再加起来显示,不假装这些数字全是自己从头数出来的。 单篇文章更麻烦。我并没有每篇文章的可靠旧计数,因此没法把八万多次平均分给所有文章,也不能把未知的旧浏览量解释成零。现在单篇页面显示的,是新系统启用后记录到的浏览量,不是这篇文章从发表至今的累计数。 页面上最后只保留数字,估算来源和单篇统计的起点放在悬停提示及隐私说明里。去掉前面的提示词,只是改了显示方式,不是把旧数据变出来了。 以后如果原服务恢复,并且确实能读到可核实的单篇记录,再单独判断能不能补回、怎样避免重复。现在没有安排自动抓取,也不为了等它恢复而停掉新的计数。这个遗憾先留着。 还有,访客数也不等于精确的人数。目前是在浏览器本地保存一个有效期为 365 天的随机编号,服务端保存它的 SHA-256 摘要。同一个编号再次访问,不再新增一个访客。 清除网站数据、更换浏览器或设备,都可能产生新的编号;旧访客与新编号之间也无法去重。浏览器禁止本地存储时,则只记浏览量,不把每次访问都硬算成一个新访客。这个数字适合大概看看,不适合拿来做人口普查。 计数数据库不保存 IP、User-Agent 或随机浏览器编号原文,也不发送网址里的查询参数和页内锚点。不过请求经过 Cloudflare,它在网络传输和安全防护时仍可能处理网络信息,不能因此说成完全没有隐私问题。换了统计方式,隐私说明也得跟着更新。 以后要记得备份 现在数字存进了自己的数据库,至少可以把它导出来了。完整备份用: npx wrangler@4.154.0

作者:hashes作者主页
在原博客阅读全文 (新标签页打开)

以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

一起聊聊 0

尊重不同观点

还没有评论。欢迎分享第一个想法。

RSS SUBSCRIPTIONS

选择 RSS 订阅

把博客汇的文章添加到你常用的阅读器。

关注文章 RSS 订阅本订阅需要登录后使用