與 umami 搏鬥 ・下

1.0k
...

  今天完成的課題:在前端顯示 umami 記錄的頁面訪問次數統計。
  新建文件的時候才發現上一篇的文件名和 postID 都打成 260827 了但是由於我已經掛好了 umami 而顯然這個訪客統計的數據也不像 waline 那麼好改反正只是一個月的差距也並不影響文章列表的排序所以我還是心平氣和地接受吧……

ʚ ⸸ ɞ

  前情:與 umami 搏鬥・上
  其實絕大部分都是 vibe coding 的產物而且這個顯示好像是很久以前搞的我都找不到提示詞了……我估計還有 antigravity 參與的部分因為我網頁端 gemini 記錄裡留的代碼和實際在用的版本完全不一樣!

前期準備

  Copy 這個方案需要你的 hugo 也部署在 vercel 上,在 hugo 的環境變量裡添加以下 4 個值:

  • UMAMI_URL 你的 umami 域名
  • UMAMI_WEBSITE_ID 你的 blog 在 umami 後臺的網頁 ID
  • UMAMI_USERNAME 你在 umami 後臺的帳號名,請設置為 sensitive
  • UMAMI_PASSWORD 你的 umami 帳號對應的密碼,請設置為 sensitive

  理論上使用環境變量不會暴露 umami 的帳號和密碼,不過還是建議定期更換密碼,更改以後手動更新 hugo 的環境變量即可。

添加 API

  找到了原來是用 sonnet 4.6 寫的(
  不是我自己寫的代碼就不獻醜了,只分享一下提示詞:

我在 vercel 上部署了 hugo 和 umami,參考官方文檔 https://docs.umami.is/docs/api/website-stats 幫我在 hugo 上顯示每個單獨頁面的訪客統計;
umami 是自行部署的,沒有 API key,需要使用 `UMAMI_USERNAME` 和 `UMAMI_PASSWORD` 環境變量作為憑據;
為驗證憑據設置緩存,避免頻繁請求登錄;
使用 vercel API 作為代理,禁止在前端暴露環境變量

  其實最主要的還是讓它看文檔,當初用 gemini 寫失敗的原因就是(至少目前版本的)umami 調用 API 需要使用 path 返回單個頁面的數據,但 gemini 的幻覺太嚴重了無論如何都要給我改成 url。但是 sonnet 5 隨便聊了兩句感覺幻覺比 gemini 還嚴重好幾倍,不確定寫代碼的水平有沒有下降……總之重點還是讀文檔,跑不起來就多換幾個 AI 互相監工一下吧(

添加 layout

  理論上做完上一步 claude 就會自己把 layout 寫完了,這裡就分享一些功能性的代碼:

數字格式化

  雖然但是這個代碼應該在搞字數統計的時候比較實用,意思是個人博客的訪問量真能達到這個量級那麼上述寫的所有部署方案應該都 cover 不住了……
  不過 k 級別的數字在統計全站訪客的時候應該還是能用上的蒽(

function fmt(n) {
  if (n == null) return '--';
  if (n >= 1000000) return (n / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
  if (n >= 1000)    return (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
  return n.toString();
}

區分單複數

  如果你也用了英文做統計單位但不想顯示 25 view 或者是 1 views……
  其實這個功能並沒有在本站用到因為我直接搞了個圖標把這個問題跳過了

function setHint(numEl, n) {
  const hint = numEl && numEl.closest('.count-wrapper').querySelector('.count-hint');
  if (hint) hint.textContent = n > 1 ? hint.textContent + 's' : hint.textContent.replace(/s$/, '');
}

  後記:拿給 gemini 校對的時候發現英語裡 0 也是複數,啊?
  總之正確的代碼應該是:

function setHint(numEl, n) {
  const hint = numEl && numEl.closest('.count-wrapper').querySelector('.count-hint');
  if (hint) {
    const baseText = hint.textContent.replace(/s$/, ''); 
    hint.textContent = n === 1 ? baseText : baseText + 's'; 
  }
}

  但實際使用的時候其實並不會真的有人看見 n = 0 的情況所以我決定不改了:)


評論

Loading comments...

提示