本文主要是采用开源项目analytics_with_cloudflare 。
analytics_with_cloudflare服务部署
下载项目文件
git clone https://github.com/yestool/analytics_with_cloudflare
安装依赖
cd analytics_with_cloudflare
npm install -g wrangler
npm install hono
登录
跳转cloudflare网页授权
npx wrangler login
创建D1数据库:[web_analytics]
数据库名称为
web_analytics
,与package.json
内保持一致
npx wrangler d1 create web_analytics
成功后显示:
✅ Successfully created DB web_analytics
[[d1_databases]]
binding = "DB" # available in your Worker on env.DB
database_name = "web_analytics"
database_id = "<unique-ID-for-your-database>"
配置worker和D1数据库绑定
将上个步骤返回的unique-ID-for-your-database
写进wrangler.toml
中
name = "analytics_with_cloudflare"
main = "src/index.ts"
compatibility_date = "2024-06-14"
[[d1_databases]]
binding = "DB" # available in your Worker on env.DB
database_name = "web_analytics"
database_id = "<unique-ID-for-your-database>"
初始化D1数据库的表结构
npm run initSql
修改src/index.ts
内容
const body = await c.req.json()
const hostname = body.hostname
const url_path = body.url
const referrer = body.referrer
const pv = body.pv
const uv = body.uv
// 添加以下两行
const spv = body.spv
const suv = body.suv
// 中间代码忽略
// 修改下面代码内容
const resData:{pv?: number, uv?: number, spv?: number, suv?: number} = {}
// 中间代码忽略
if (uv){
const total = await c.env.DB.prepare('SELECT COUNT(*) AS total from (select DISTINCT visitor_ip from t_web_visitor where website_id = ? and url_path = ?) t').bind(websiteId, url_path).first('total');
resData['uv'] = Number(total)
}
// 添加以下两段代码
if (spv){
const total = await c.env.DB.prepare('SELECT COUNT(*) AS total from t_web_visitor where website_id = ?').bind(websiteId).first('total');
resData['spv'] = Number(total)
}
if (suv){
const total = await c.env.DB.prepare('SELECT COUNT(*) AS total from (select DISTINCT visitor_ip from t_web_visitor where website_id = ?) t').bind(websiteId).first('total');
resData['suv'] = Number(total)
}
// 修改完成
完整代码内容,可以下载index.ts
原来的代码只支撑返回单个页面的访问数据,不支持返回整个网站的访问数据,增加spv
和suv
两个返回值,分别是全站访问人次
和全站访问人数
。
发布
npm run deploy
成功后显示:
> [email protected] deploy
> wrangler deploy
Proxy environment variables detected. We'll use your proxy for fetch requests.
⛅️ wrangler 3.18.0
-------------------
Your worker has access to the following bindings:
- D1 Databases:
- DB: web_analytics (<unique-ID-for-your-database>)
Total Upload: 50.28 KiB / gzip: 12.23 KiB
Uploaded analytics_with_cloudflare (1.29 sec)
Published analytics_with_cloudflare (4.03 sec)
https://analytics_with_cloudflare.xxxxx.workers.dev
Current Deployment ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
添加自定义域名
可能cloudflare的workers.dev
域名在有些区域里不能显示,可以在cloudflare配置自定义域名。
进入Workers 和 Pages
->analytics_with_cloudflare(默认,改成你修改后名称)
->设置
->域和路由
->添加
,选择自定义域
,输入二级域名即可,Cloudflare会自动在响应的主域名下添加一个对应二级域名的worker类型的DNS记录。
Hugo集成配置
博客目录参考如下:
bytejog
├── archetypes # 站点文章模板
│ └── default.md
├── assets # 需要被处理的资源 (SCSS、TypeScrpt 等)
│ └── js
├── content # 站点文章
├── data # 配置文件
├── hugo.yaml # 站点配置文件
├── i18n # 国际化文件
├── layouts # 站点样式
│ └── partials
├── static # 静态资源 (图片、CSS、JavaScript 等)
└── themes # 主题
analytics_with_cloudflare前端部署修改
将analytics_with_cloudflare的front/dist
目录下的index.js
文件拷贝到hugo目录的assets/js
目录下,修改名字为analytics.js
。
修改js文件的内容,以支撑前面后端服务增加返回的全站访问人次
和全站访问人数
数据。
修改内容如下:
(function(){
console.info(
'welcome to WebViso/yestool,author:YesTool,author url: https://webviso.yestool.org'
);
setTimeout(function () {
var addHeadStr = '<meta property="og:site_counter_author" content="yestool"></meta>'
+ '<meta property="og:site_counter_author_url" content="https://webviso.yestool.org"></meta>';
if (document.head){
document.head.innerHTML += addHeadStr;
}
}, 500);
const script = document.currentScript;
let dataBaseUrl = script.getAttribute('data-base-url');
let dataPagePvId = script.getAttribute('data-page-pv-id');
let dataPageUvId = script.getAttribute('data-page-uv-id');
let dataSitePvId = script.getAttribute('data-site-pv-id');
let dataSiteUvId = script.getAttribute('data-site-uv-id');
const WebViso = {};
WebViso.version = '0.0.0';
let BASE_API_PATH = 'https://webviso.yestool.org';
WebViso.page_pv_id = "page_pv";
WebViso.page_uv_id = "page_uv";
WebViso.site_pv_id = "site_pv";
WebViso.site_uv_id = "site_uv";
if(dataBaseUrl) {
BASE_API_PATH = dataBaseUrl;
}
if(dataPagePvId) {
WebViso.page_pv_id = dataPagePvId;
}
if(dataPageUvId) {
WebViso.page_uv_id = dataPageUvId;
}
if(dataSitePvId) {
WebViso.site_pv_id = dataSitePvId;
}
if(dataSiteUvId) {
WebViso.site_uv_id = dataSiteUvId;
}
/**
* @description: init Fetch json from api
* @return {Object}
*/
WebViso.init = async function () {
const thisPage = getLocation(window.location.href);
const pagePvEle = document.getElementById(WebViso.page_pv_id);
const pageUvEle = document.getElementById(WebViso.page_uv_id);
const sitePvEle = document.getElementById(WebViso.site_pv_id);
const siteUvEle = document.getElementById(WebViso.site_uv_id);
const queryData = {
url: thisPage.pathname,
hostname: thisPage.hostname,
referrer: document.referrer
}
if (pagePvEle) {
queryData.pv = true;
}
if (pageUvEle) {
queryData.uv = true;
}
if (sitePvEle) {
queryData.spv = true;
}
if (siteUvEle) {
queryData.suv = true;
}
await fetchJson(`${BASE_API_PATH}/api/visit`, queryData)
.then((res) => {
if (res.ret != 'OK') {
console.error('WebViso.init error', res.message);
return;
}
const resData = res.data;
if (pagePvEle) {
pagePvEle.innerText = resData.pv;
}
if (pageUvEle) {
pageUvEle.innerText = resData.uv;
}
if (sitePvEle) {
sitePvEle.innerText = resData.spv;
}
if (siteUvEle) {
siteUvEle.innerText = resData.suv;
}
})
.catch((err) => {
console.log("WebViso.init fetch error", err);
});
};
/**
* @description: Fetch json from api
* @param {String} url response from url
* @return {Object}
*/
function fetchJson(url, data) {
return new Promise((resolve) => {
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then(res => {
return res.json();
})
.then(function(data) {
resolve(data);
})
.catch(err => {
console.error(err);
});
});
}
const getLocation = function(href) {
const l = document.createElement("a");
l.href = href;
return l;
};
if (typeof window !== 'undefined') {
WebViso.init();
window.WebViso = WebViso;
}
})();
header扩展修改
hugo目录的layouts/partials
目录下新建extend_head.html
,文件内容为:
{{- /* Head custom content area start */ -}}
{{- /* Insert any custom code (web-analytics, resources, etc.) - it will appear in the <head></head> section of every page. */ -}}
{{- /* Can be overwritten by partial with the same name in the global layouts. */ -}}
{{- /* Head custom content area end */ -}}
{{ $analyticsJs := resources.Get "js/analytics.js" | js.Build "analytics.js" | minify | fingerprint }}
<script defer src="{{ $analyticsJs.Permalink }}" data-base-url="https://xxxxxxxxxxxxxxxxxxxxxxxxxxxx" data-page-pv-id="page_pv" data-page-uv-id="page_uv" data-site-pv-id="site_pv" data-site-uv-id="site_uv"></script>
url
的内容替换为二级域名,或者worker的域名https://analytics_with_cloudflare.xxxxx.workers.dev
footer扩展修改
hugo目录的layouts/partials
目录下新建extend_footer.html
,文件内容为:
{{- /* Footer custom content area start */ -}}
{{- /* Insert any custom code web-analytics, resources, etc. here */ -}}
{{- /* Footer custom content area end */ -}}
<div class="footer" style="padding-top: 0px;margin-top: -18px;">
本站总访问量<span id="site_pv"></span>次
</div>
加入id为page_pv
、 page_uv
、site_pv
或 site_uv
的标签,即可显示 访问人次(pv)
或、访问人数(uv)
、总访问人次(spv)
或 总访问人数(suv)
本页访问人次:<span id="page_pv"></span>
本页访问人数:<span id="page_uv"></span>
本站总访问人次:<span id="site_pv"></span>
本站总访问人数:<span id="site_uv"></span>