Files
statistics/main.js
2024-12-16 07:42:09 +08:00

148 lines
5.2 KiB
JavaScript

import fs from 'fs'
import { Tail } from 'tail'
import { update } from './update.js'
class 计数 {
= new Map()
= new Map()
= new Map()
= new Map()
}
class 类型 {
浏览数 = new 计数()
评论数 = new 计数()
点赞数 = new 计数()
收藏数 = new 计数()
搜索词 = new 计数()
}
const 文章 = new 类型()
const 作品 = new 类型()
const 游戏 = new 类型()
const 截图 = new 类型()
const 收藏 = new 类型()
const 综合 = new 类型()
const 日期格式 = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
})
// 匹配文章和截图的正则表达式
const imagesRegex = /\/web\/v1\/images\/detail\/v2\?imagesId=(\d+)&currentUserId=(\d+)/
const articleRegex = /\/web\/v1\/article\/get\?id=(\d+)&userId=(\d+)/
const collectionRegex = /\/web\/v1\/member\/explorer\/article\/get\?id=(\d+)/
var 当前日期 = 日期格式.format(new Date()).replace(/\//g, '-')
var 正在存档 = false
const 存档 = async (message = '正在存档...') => {
if (正在存档) return
正在存档 = true
console.log(message)
await fs.promises.writeFile(`./data/${当前日期}.json`, JSON.stringify({
文章: Object.fromEntries(文章.浏览数.),
截图: Object.fromEntries(截图.浏览数.),
收藏: Object.fromEntries(收藏.浏览数.),
文章搜索: Object.fromEntries(文章.搜索词.),
截图搜索: Object.fromEntries(截图.搜索词.),
收藏搜索: Object.fromEntries(收藏.搜索词.),
}, null, 2))
正在存档 = false
}
export default function () {
console.log(当前日期, '启动时加载当日数据...')
if (fs.existsSync(`./data/${当前日期}.json`)) {
const file = fs.readFileSync(`./data/${当前日期}.json`, 'utf8')
if (file) {
const data = JSON.parse(file)
Object.entries(data.文章).forEach(([key, value]) => 文章.浏览数..set(key, value))
Object.entries(data.截图).forEach(([key, value]) => 截图.浏览数..set(key, value))
Object.entries(data.收藏).forEach(([key, value]) => 收藏.浏览数..set(key, value))
Object.entries(data.文章搜索).forEach(([key, value]) => 文章.搜索词..set(key, value))
Object.entries(data.截图搜索).forEach(([key, value]) => 截图.搜索词..set(key, value))
Object.entries(data.收藏搜索).forEach(([key, value]) => 收藏.搜索词..set(key, value))
}
}
setInterval(async () => {
存档('每10分钟自动存档...')
await update('web_images', 'day_rank', Object.entries(Object.fromEntries(截图.浏览数.)))
await update('web_article', 'day_rank', Object.entries(Object.fromEntries(文章.浏览数.)))
await update_explorer('web_member_explorer', 'day_rank', Object.entries(Object.fromEntries(收藏.浏览数.)))
}, 20)
console.log(当前日期, '开始收集日志...')
const tail = new Tail('/opt/log/caddy/access.log')
tail.on('line', async (line) => {
const item = JSON.parse(line)
if (item.level !== 'debug') return
if (item.msg !== 'upstream roundtrip') return
// 检查日志的日期是否跨天
const logDate = 日期格式.format(new Date(item.ts * 1000)).replace(/\//g, '-')
if (当前日期 !== logDate) {
await 存档('跨日期存档...')
文章.浏览数..clear()
截图.浏览数..clear()
收藏.浏览数..clear()
文章.搜索词..clear()
截图.搜索词..clear()
收藏.搜索词..clear()
当前日期 = logDate
}
// 处理文章日志
if (item.request.uri.startsWith('/web/v1/article/get')) {
const [uri, id] = item.request.uri.match(articleRegex) ?? []
if (uri && id) {
文章.浏览数..set(id, 文章.浏览数..has(id) ? 文章.浏览数..get(id) + 1 : 1)
}
}
// 处理截图日志
if (item.request.uri.startsWith('/web/v1/images/detail/v2')) {
const [uri, id] = item.request.uri.match(imagesRegex) ?? []
if (uri && id) {
截图.浏览数..set(id, 截图.浏览数..has(id) ? 截图.浏览数..get(id) + 1 : 1)
}
return
}
// 处理收藏日志
if (item.request.uri.startsWith('/web/v1/member/explorer/article/get')) {
const [uri, id] = item.request.uri.match(collectionRegex) ?? []
if (uri && id) {
收藏.浏览数..set(id, 收藏.浏览数..has(id) ? 收藏.浏览数..get(id) + 1 : 1)
}
return
}
})
// 日志尾部事件监听
tail.on('end', () => console.log('没有更多内容,停止读取'))
tail.on('error', (error) => console.error('错误:', error))
// 捕获退出信号,确保数据存档
process.on('SIGINT', async () => {
tail.unwatch() // 停止监听日志
await archive('退出前存档数据...')
process.exit()
})
}