diff --git a/_config.yml b/_config.yml
index c392d668..5f253913 100644
--- a/_config.yml
+++ b/_config.yml
@@ -260,21 +260,6 @@ web_analytics:
google:
measurement_id:
- # Umami Analytics,src 和 data-website-id 是必填项,其他可选
- # Umami Analytics, src and data-website-id are required, others are optional
- umami:
- # umami js 文件地址,需要在 umami 后台创建站点后获取
- # umami js file url, get after create website in umami
- src:
- # umami 的 website id,需要在 umami 后台创建站点后获取
- # umami website id, get after create website in umami
- data_website_id:
- # 如果你只想跟踪器跟踪特定的域名,你可以将它们添加到你的跟踪器脚本中。这是一个逗号分隔的域名列表。
- # 如果你在一个分阶段、开发的环境中工作,这会很有帮助。避免统计 localhost。
- # If you want the tracker to only run on specific domains, you can add them to your tracker script. This is a comma delimited list of domain names.
- # Helps if you are working in a staging/development environment. Avoids tracking localhost.
- data_domains:
-
# 腾讯统计的 H5 App ID,开启高级功能才有cid
# Tencent analytics, set APP ID
# See: https://mta.qq.com/h5/manage/ctr_app_manage
@@ -307,6 +292,34 @@ web_analytics:
# If true, ignore localhost & 127.0.0.1
ignore_local: false
+ # Umami Analytics,仅支持自部署。如果要展示 PV UV 需要填写所有配置项,否则只填写 `src` 和 `website_id` 即可
+ # Umami Analytics, only Self-host support. If you want to display PV UV need to set all config items, otherwise only set 'src' and 'website_id'
+ # See: https://umami.is/docs/authentication
+ umami:
+ # umami js 文件地址,需要在 umami 后台创建站点后获取
+ # umami js file url, get after create website in umami
+ src:
+
+ # umami 的 website id,需要在 umami 后台创建站点后获取
+ # umami website id, get after create website in umami
+ website_id:
+
+ # 如果你只想统计特定的域名可以填入此字段,多个域名通过逗号分隔,这可以避免统计 localhost。
+ # If you only want to tracker to specific domains you can fill in this field, multiple domain names are separated by commas, which can avoid tracker localhost
+ domains:
+
+ # 用于统计 PV UV 的开始时间,格式为 "YYYY-MM-DD"
+ # statistics on the start time, the format is "YYYY-MM-DD"
+ start_time: 2024-01-01
+
+ # 新建一个 umami viewOnly 用户,然后通过 login api 获取该用户 token
+ # create an umami viewOnly user, and then get user token through the login api
+ token:
+
+ # 填写 umami 部署的服务器地址,如 "https://umami.example.com"
+ # server url of umami deployment, such as "https://umami.example.com"
+ api_server:
+
# Canonical 用于向 Google 搜索指定规范网址,开启前确保 hexo _config.yml 中配置 `url: http://yourdomain.com`
# Canonical, to specify a canonical URL for Google Search, need to set up `url: http://yourdomain.com` in hexo _config.yml
# See: https://support.google.com/webmasters/answer/139066
@@ -453,9 +466,9 @@ footer:
statistics:
enable: false
- # 统计数据来源,使用 leancloud 需要设置 `web_analytics: leancloud` 中的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
- # Data source. If use leancloud, you need to set the parameter in `web_analytics: leancloud`
- # Options: busuanzi | leancloud
+ # 统计数据来源,使用 leancloud, umami 需要设置 `web_analytics` 中对应的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
+ # Data source. If use leancloud, umami, you need to set the parameter in `web_analytics`
+ # Options: busuanzi | leancloud | umami
source: "busuanzi"
# 国内大陆服务器的备案信息
@@ -598,7 +611,7 @@ post:
enable: false
# 统计数据来源
# Data Source
- # Options: busuanzi | leancloud
+ # Options: busuanzi | leancloud | umami
source: "busuanzi"
# 在文章开头显示文章更新时间,该时间默认是 md 文件更新时间,可通过 front-matter 中 `updated` 手动指定(和 date 一样格式)
diff --git a/layout/_partials/footer/statistics.ejs b/layout/_partials/footer/statistics.ejs
index 84f061b1..9f05e2dc 100644
--- a/layout/_partials/footer/statistics.ejs
+++ b/layout/_partials/footer/statistics.ejs
@@ -1,6 +1,6 @@
- <% var pv_texts = (theme.footer.statistics.pv_format || __('footer.pv')).split('{}') %>
- <% var uv_texts = (theme.footer.statistics.uv_format || __('footer.uv')).split('{}') %>
+ <% let pv_texts = (theme.footer.statistics.pv_format || __('footer.pv')).split('{}') %>
+ <% let uv_texts = (theme.footer.statistics.uv_format || __('footer.uv')).split('{}') %>
<% if (theme.footer.statistics.source === 'leancloud') { %>
<% if (pv_texts.length >= 2) { %>
@@ -35,5 +35,23 @@
<% } %>
<% import_js(theme.static_prefix.busuanzi, 'busuanzi.pure.mini.js', 'defer') %>
+
+ <% } else if (theme.footer.statistics.source === 'umami') { %>
+ <% if (pv_texts.length >= 2) { %>
+
+ <%- pv_texts[0] %>
+
+ <%- pv_texts[1] %>
+
+ <% } %>
+ <% if (uv_texts.length >= 2) { %>
+
+ <%- uv_texts[0] %>
+
+ <%- uv_texts[1] %>
+
+ <% } %>
+ <% import_js(theme.static_prefix.internal_js, 'umami-view.js', 'defer') %>
<% } %>
+
diff --git a/layout/_partials/plugins/analytics.ejs b/layout/_partials/plugins/analytics.ejs
index b8392070..dd5c1a6b 100644
--- a/layout/_partials/plugins/analytics.ejs
+++ b/layout/_partials/plugins/analytics.ejs
@@ -31,12 +31,17 @@
<% } %>
- <% if(theme.web_analytics.umami && theme.web_analytics.umami.src && theme.web_analytics.umami.data_website_id) { %>
-
-
+ <% if(theme.web_analytics.umami && theme.web_analytics.umami.src && theme.web_analytics.umami.website_id) { %>
+
<% } %>
<% if(theme.web_analytics.tencent && theme.web_analytics.tencent.sid && theme.web_analytics.tencent.cid) { %>
diff --git a/layout/_partials/post/meta-top.ejs b/layout/_partials/post/meta-top.ejs
index b94e15c9..3e2e5452 100644
--- a/layout/_partials/post/meta-top.ejs
+++ b/layout/_partials/post/meta-top.ejs
@@ -51,12 +51,20 @@
<%- views_texts[0] %><%- views_texts[1] %>
<% import_js(theme.static_prefix.internal_js, 'leancloud.js', 'defer') %>
+
<% } else if (theme.post.meta.views.source === 'busuanzi') { %>
<%- views_texts[0] %><%- views_texts[1] %>
<% import_js(theme.static_prefix.busuanzi, 'busuanzi.pure.mini.js', 'defer') %>
+
+ <% } else if (theme.post.meta.views.source === 'umami') { %>
+
+
+ <%- views_texts[0] %><%- views_texts[1] %>
+
+ <% import_js(theme.static_prefix.internal_js, 'umami-view.js', 'defer') %>
<% } %>
<% } %>
diff --git a/source/js/umami-view.js b/source/js/umami-view.js
new file mode 100644
index 00000000..6ee3f6a8
--- /dev/null
+++ b/source/js/umami-view.js
@@ -0,0 +1,99 @@
+// 从配置文件中获取 umami 的配置
+const website_id = CONFIG.web_analytics.umami.website_id;
+// 拼接请求地址
+const request_url = `${CONFIG.web_analytics.umami.api_server}/websites/${website_id}/stats`;
+
+const start_time = new Date(CONFIG.web_analytics.umami.start_time).getTime();
+const end_time = new Date().getTime();
+const token = CONFIG.web_analytics.umami.token;
+
+// 检查配置是否为空
+if (!website_id) {
+ throw new Error("Umami website_id is empty");
+}
+if (!request_url) {
+ throw new Error("Umami request_url is empty");
+}
+if (!start_time) {
+ throw new Error("Umami start_time is empty");
+}
+if (!token) {
+ throw new Error("Umami token is empty");
+}
+
+// 构造请求参数
+const params = new URLSearchParams({
+ startAt: start_time,
+ endAt: end_time,
+});
+// 构造请求头
+const request_header = {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ "x-umami-api-key": "oZKCH3msvqt10VlXKwoJvHclmaS4bVx0",
+ },
+};
+
+// 获取站点统计数据
+async function siteStats() {
+ try {
+ const response = await fetch(`${request_url}?${params}`, request_header);
+ const data = await response.json();
+ const uniqueVisitors = data.uniques.value; // 获取独立访客数
+ const pageViews = data.pageviews.value; // 获取页面浏览量
+
+ let pvCtn = document.querySelector("#umami-site-pv-container");
+ if (pvCtn) {
+ let ele = document.querySelector("#umami-site-pv");
+ if (ele) {
+ ele.textContent = pageViews; // 设置页面浏览量
+ pvCtn.style.display = "inline"; // 将元素显示出来
+ }
+ }
+
+ let uvCtn = document.querySelector("#umami-site-uv-container");
+ if (uvCtn) {
+ let ele = document.querySelector("#umami-site-uv");
+ if (ele) {
+ ele.textContent = uniqueVisitors;
+ uvCtn.style.display = "inline";
+ }
+ }
+ } catch (error) {
+ console.error(error);
+ return "-1";
+ }
+}
+
+// 获取页面浏览量
+async function pageStats(path) {
+ try {
+ const response = await fetch(`${request_url}?${params}&url=${path}`, request_header);
+ const data = await response.json();
+ const pageViews = data.pageviews.value;
+
+ let viewCtn = document.querySelector("#umami-page-views-container");
+ if (viewCtn) {
+ let ele = document.querySelector("#umami-page-views");
+ if (ele) {
+ ele.textContent = pageViews;
+ viewCtn.style.display = "inline";
+ }
+ }
+ } catch (error) {
+ console.error(error);
+ return "-1";
+ }
+}
+
+siteStats();
+
+// 获取页面容器
+let viewCtn = document.querySelector("#umami-page-views-container");
+// 如果页面容器存在,则获取页面浏览量
+if (viewCtn) {
+ let path = window.location.pathname;
+ let target = decodeURI(path.replace(/\/*(index.html)?$/, "/"));
+ pageStats(target);
+}