Skip to content

Commit c1c9baf

Browse files
committed
feat: Web analytics 新增 supabase
1 parent ba7d678 commit c1c9baf

File tree

4 files changed

+151
-3
lines changed

4 files changed

+151
-3
lines changed

_config.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,16 @@ web_analytics:
310310
# server url of umami deployment, such as "https://umami.example.com"
311311
api_server:
312312

313+
supabase:
314+
server_url:
315+
anon_key:
316+
# 统计页面时获取路径的属性
317+
# Get the attribute of the page path during statistics
318+
path: window.location.pathname
319+
# 开启后不统计本地路径( localhost 与 127.0.0.1 )
320+
# If true, ignore localhost & 127.0.0.1
321+
ignore_local: true
322+
313323
# Canonical 用于向 Google 搜索指定规范网址,开启前确保 hexo _config.yml 中配置 `url: http://yourdomain.com`
314324
# Canonical, to specify a canonical URL for Google Search, need to set up `url: http://yourdomain.com` in hexo _config.yml
315325
# See: https://support.google.com/webmasters/answer/139066
@@ -456,9 +466,9 @@ footer:
456466
statistics:
457467
enable: false
458468

459-
# 统计数据来源,使用 leancloud, umami 需要设置 `web_analytics` 中对应的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
469+
# 统计数据来源,使用 leancloud, umami, supabase 需要设置 `web_analytics` 中对应的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
460470
# Data source. If use leancloud, umami, you need to set the parameter in `web_analytics`
461-
# Options: busuanzi | leancloud | umami
471+
# Options: busuanzi | leancloud | umami | supabase
462472
source: "busuanzi"
463473

464474
# 国内大陆服务器的备案信息
@@ -601,7 +611,7 @@ post:
601611
enable: false
602612
# 统计数据来源
603613
# Data Source
604-
# Options: busuanzi | leancloud | umami
614+
# Options: busuanzi | leancloud | umami | supabase
605615
source: "busuanzi"
606616

607617
# 在文章开头显示文章更新时间,该时间默认是 md 文件更新时间,可通过 front-matter 中 `updated` 手动指定(和 date 一样格式)

layout/_partials/footer/statistics.ejs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@
5252
</span>
5353
<% } %>
5454
<% import_js(theme.static_prefix.internal_js, 'umami-view.js', 'defer') %>
55+
56+
<% } else if (theme.footer.statistics.source === 'supabase') { %>
57+
<% if (pv_texts.length >= 2) { %>
58+
<span id="supabase-site-pv-container" style="display: none">
59+
<%- pv_texts[0] %>
60+
<span id="supabase-site-pv"></span>
61+
<%- pv_texts[1] %>
62+
</span>
63+
<% } %>
64+
<% if (uv_texts.length >= 2) { %>
65+
<span id="supabase-site-uv-container" style="display: none">
66+
<%- uv_texts[0] %>
67+
<span id="supabase-site-uv"></span>
68+
<%- uv_texts[1] %>
69+
</span>
70+
<% } %>
71+
<% import_js(theme.static_prefix.internal_js, 'supabase.js', 'defer') %>
5572
<% } %>
5673

5774
</div>

layout/_partials/post/meta-top.ejs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@
6565
<%- views_texts[0] %><span id="umami-page-views"></span><%- views_texts[1] %>
6666
</span>
6767
<% import_js(theme.static_prefix.internal_js, 'umami-view.js', 'defer') %>
68+
69+
<% } else if (theme.post.meta.views.source === 'supabase') { %>
70+
<span id="supabase-page-views-container" style="display: none">
71+
<i class="iconfont icon-eye" aria-hidden="true"></i>
72+
<%- views_texts[0] %><span id="supabase-page-views"></span><%- views_texts[1] %>
73+
</span>
74+
<% import_js(theme.static_prefix.internal_js, 'supabase.js', 'defer') %>
6875
<% } %>
6976
<% } %>
7077
</div>

source/js/supabase.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/* global CONFIG */
2+
// eslint-disable-next-line no-console
3+
4+
(function(window, document) {
5+
var supabaseUrl = CONFIG.web_analytics.supabase.server_url;
6+
var supabaseKey = CONFIG.web_analytics.supabase.anon_key;
7+
8+
if (!supabaseUrl) {
9+
throw new Error('Supabase serverUrl is empty');
10+
}
11+
if (!supabaseKey) {
12+
throw new Error('Supabase anonKey is empty');
13+
}
14+
15+
// 参数: target (slug), amount (增加的数量,1 或 0)
16+
// 这个 RPC 调用同时包含了更新和查询逻辑
17+
function updateCounter(target, amount) {
18+
return fetch(`${supabaseUrl}/rest/v1/rpc/update_counter`, {
19+
method: 'POST',
20+
headers: {
21+
'apikey': supabaseKey,
22+
'Authorization': `Bearer ${supabaseKey}`,
23+
'Content-Type': 'application/json',
24+
},
25+
body: JSON.stringify({
26+
slug_input: target,
27+
increment_amount: amount
28+
})
29+
})
30+
.then(res => res.json())
31+
.then(data => data)
32+
.catch(err => {
33+
console.error('Supabase Error:', err);
34+
return 0;
35+
});
36+
}
37+
38+
// 校验是否为有效的 Host
39+
function validHost() {
40+
if (window.location.protocol === 'file:') {
41+
return false;
42+
}
43+
44+
if (CONFIG.web_analytics.supabase.ignore_local) {
45+
var hostname = window.location.hostname;
46+
if (hostname === 'localhost' || hostname === '127.0.0.1') {
47+
return false;
48+
}
49+
}
50+
return true;
51+
}
52+
53+
// 校验是否为有效的 UV
54+
function validUV() {
55+
var key = 'supabase_UV_Flag';
56+
var flag = localStorage.getItem(key);
57+
if (flag) {
58+
// 距离标记小于 24 小时则不计为 UV
59+
if (new Date().getTime() - parseInt(flag, 10) <= 86400000) {
60+
return false;
61+
}
62+
}
63+
localStorage.setItem(key, new Date().getTime().toString());
64+
return true;
65+
}
66+
67+
function addCount() {
68+
var enableIncr = CONFIG.web_analytics.enable && !Fluid.ctx.dnt && validHost();
69+
70+
// 请求 PV 并自增
71+
var pvCtn = document.querySelector('#supabase-site-pv-container');
72+
if (pvCtn) {
73+
// 如果允许统计则 +1,否则 +0 (只读)
74+
var amount = enableIncr ? 1 : 0;
75+
updateCounter('site-pv', amount).then(count => {
76+
var ele = document.querySelector('#supabase-site-pv');
77+
if (ele) {
78+
ele.innerText = count;
79+
pvCtn.style.display = 'inline';
80+
}
81+
});
82+
}
83+
84+
// 请求 UV 并自增
85+
var uvCtn = document.querySelector('#supabase-site-uv-container');
86+
if (uvCtn) {
87+
var amount = (enableIncr && validUV()) ? 1 : 0;
88+
updateCounter('site-uv', amount).then(count => {
89+
var ele = document.querySelector('#supabase-site-uv');
90+
if (ele) {
91+
ele.innerText = count;
92+
uvCtn.style.display = 'inline';
93+
}
94+
});
95+
}
96+
97+
// 如果有页面浏览数节点,则请求浏览数并自增
98+
var viewCtn = document.querySelector('#supabase-page-views-container');
99+
if (viewCtn) {
100+
var path = eval(CONFIG.web_analytics.supabase.path || 'window.location.pathname');
101+
var target = decodeURI(path.replace(/\/*(index.html)?$/, '/'));
102+
103+
var amount = enableIncr ? 1 : 0;
104+
updateCounter(target, amount).then(count => {
105+
var ele = document.querySelector('#supabase-page-views');
106+
if (ele) {
107+
ele.innerText = count;
108+
viewCtn.style.display = 'inline';
109+
}
110+
});
111+
}
112+
}
113+
addCount();
114+
})(window, document);

0 commit comments

Comments
 (0)