Cloudflare Worker implementation of a Countflare page counter with busuanzi-compatible endpoints.
It provides:
site_pv: total site page viewssite_uv: unique site visitors, based on a lightweight 128-bit xxHash-style visitor keypage_pv: current page viewspage_uv: current page unique visitors
/js: browser script for Hexo Butterfly and normal busuanzi/Countflare DOM ids/busuanzi/2.3/busuanzi.pure.mini.js: compatibility script path/busuanzi?jsonpCallback=...&url=...: JSONP API/busuanzi?url=...: JSON API/busuanzi?mode=read&url=...: read counters without incrementing/admin: GitHub-login management dashboard
npm install
npm run deployCreate a GitHub OAuth App, then set these Worker secrets:
npx wrangler secret put GITHUB_CLIENT_ID
npx wrangler secret put GITHUB_CLIENT_SECRET
npx wrangler secret put SESSION_SECRET
npx wrangler secret put ADMIN_EMAILSet ADMIN_EMAIL to the verified GitHub email that should become the system administrator, for example ljing431@163.com.
For the GitHub OAuth App:
- Homepage URL:
https://<your-worker-domain> - Authorization callback URL:
https://<your-worker-domain>/auth/github/callback
Users sign in with GitHub. The Worker reads the account's verified email, creates a local account, and uses that email for ownership and admin checks.
After deploy, update Butterfly's _config.yml:
CDN:
option:
busuanzi: https://<your-worker-domain>/jsThe current local Butterfly theme already enables:
busuanzi:
site_uv: true
site_pv: true
page_pv: trueEdit busuanzi.config.js in the project root:
export default {
migrationUrl: "https://events.vercount.one/api/v2/log",
allowOrigins: [
{
origin: "blog.krins.cloud",
aggregateOrigins: ["krins.cloud", "www.krins.cloud"],
migrationUrl: "https://example.com/krins-seed",
},
],
};allowOrigins controls which sites can use the API. An empty array allows all sites. Each entry may be a string:
allowOrigins: ["blog.krins.cloud"]or an object with aggregateOrigins. If blog.krins.cloud aggregates krins.cloud and www.krins.cloud, requests from any of those hosts are stored under the actual request host, then the response sums all three hosts. Removing the aggregate config later leaves the original per-host data intact.
migrationUrl is optional. When a site or page record does not exist yet, the Worker tries to fetch seed data before applying the current visit. It appends site and page query parameters:
https://example.com/busuanzi-seed?site=blog.krins.cloud&page=%2Fposts%2Fdemo
You can also use placeholders:
migrationUrl: "https://example.com/seed/{site}/{page}"The current Butterfly config previously used https://cn.vercount.one/js. That script calls https://events.vercount.one/api/v2/log, so the root config is set to that Vercount API URL for migration.
Expected JSON may be either page-specific:
{
"site_pv": 1000,
"site_uv": 200,
"page_pv": 30,
"page_uv": 12
}/admin provides:
- GitHub login and verified-email account creation
- user-owned domain creation
- DNS TXT ownership verification
- per-domain trend charts with selectable ranges, totals, and top pages
- aggregate origin settings per domain
- admin-only user blocking and domain status changes
- admin-only counter editing
TXT verification uses:
Record: _countflare.<your-domain>
Type: TXT
Value: countflare-verify=<token shown in /admin>
The previous _busuanzi / busuanzi-verify TXT format is still accepted for existing records.
Only active domains can call the public Countflare API. pending and blocked domains are rejected.
or include a pages map:
{
"site_pv": 1000,
"site_uv": 200,
"pages": {
"/posts/demo": {
"page_pv": 30,
"page_uv": 12
}
}
}npm install
npm run dev
npm testThen point Butterfly to the local script if you want to test in the blog:
CDN:
option:
busuanzi: http://127.0.0.1:8787/js- Counters are stored in one Durable Object per site host.
- Aggregate origins are summed at read time only. Writes remain per host.
- Raw IP addresses are not stored. The Worker stores only 128-bit xxHash-based visitor identifiers.
- Visitor identifiers use a lightweight non-cryptographic hash for counting. Admin sessions still use Web Crypto HMAC because that is security-sensitive.
- The API supports both
jsonpCallbackandcallbackquery parameters.