给发布服务添加统计访问量及网站运行时间
see https://ld246.com/article/1744513615105
// 统计网站访问量和运行时间
(async ()=>{
// api地址,最后不要加 /
const apiUrl = 'http://127.0.0.1:6806';
// api token 在设置->关于中查看
const apiToken = '';
// 初始化数据
const initData = {
// 网站总访问量
total: 0,
// 网站运行初始日期
startDay: '2025-04-13'
};
// 定义统计信息显示模板
const tongjiTpl = `总访问量:{total} 网站运行:{runningDays}天`;
// 已统计的跳过
if(document.querySelector('#status .site__tongji')) return;
// 获取置顶数据,格式 {"total":0, "startDay":''}
let data = await getFile('/data/storage/site_tongji.json');
data = JSON.parse(data||'{}');
if(data.code && data.code !== 0) data = {};
data = {...initData, ...data};
// 仅发布服务器才统计
if(siyuan.config.readonly) {
// 网站访问+1
data.total++;
// 存储网站访问数据
putFile('/data/storage/site_tongji.json', JSON.stringify(data, null, 4));
}
// 获取网站运行时间
const runningDays = calculateRunningDays(data);
// 状态栏显示统计信息
showStatusMsg(tongjiTpl.replace('{total}', data.total).replace('{runningDays}', runningDays));
// 计算网站运行时间(单位:天)
function calculateRunningDays(data) {
// 获取当前日期并重置时间为 00:00:00
const currentDate = new Date();
resetTime(currentDate);
// 解析初始日期并重置时间为 00:00:00
const startDate = new Date(data.startDay);
if (isNaN(startDate.getTime())) {
return 1;
}
resetTime(startDate);
// 计算时间差(毫秒)
const timeDifference = currentDate - startDate;
// 转换为天数
const daysDifference = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
return daysDifference + 1;
}
function resetTime(date) {
date.setHours(0, 0, 0, 0); // 设置时间为 00:00:00.000
return date;
}
// 状态栏输出
function showStatusMsg(html) {
const statusMsg = document.querySelector('#status .status__msg');
if(!statusMsg) return;
const tongji = document.querySelector('#status .site__tongji');
if(tongji) tongji.remove();
const style = `
color: var(--b3-theme-on-surface);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
padding-left: 5px;
font-size: 12px;
`;
html = `<div class="site__tongji" style="${style}">${html}</div>`;
statusMsg.insertAdjacentHTML('beforebegin', html);
}
// 获取文件
async function getFile(path) {
return fetch("/api/file/getFile", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
path,
}),
}).then((response) => {
if (response.ok) {
return response.text();
} else {
throw new Error("Failed to get file content");
}
}).catch((error) => {
console.error(error);
});
}
// 存储文件,支持创建文件夹,当isDir true时创建文件夹,忽略文件
async function putFile(path, content = '', isDir = false) {
const formData = new FormData();
formData.append("path", path);
formData.append("isDir", isDir)
formData.append("file", new Blob([content]));
const result = await fetch(apiUrl+"/api/file/putFile", { // 写入js到本地
method: "POST",
headers: {
"Authorization": "token " + apiToken // 添加 Authorization 头
},
body: formData,
});
const json = await result.json();
return json;
}
})();
