基本实现
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
access.log
|
access.log
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
data
|
||||||
|
@@ -5,3 +5,10 @@
|
|||||||
引入数据
|
引入数据
|
||||||
创建指标
|
创建指标
|
||||||
导出接口
|
导出接口
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
|
node main.js
|
||||||
|
```
|
||||||
|
|
||||||
|
63
main.js
63
main.js
@@ -1,22 +1,69 @@
|
|||||||
|
import fs from 'fs'
|
||||||
import { Tail } from 'tail'
|
import { Tail } from 'tail'
|
||||||
|
|
||||||
const tail = new Tail('./access.log')
|
const tail = new Tail('/opt/log/caddy/access.log')
|
||||||
|
|
||||||
// 指标(日榜, 周榜, 月榜, 总榜)
|
// 指标(日榜, 周榜, 月榜, 总榜)
|
||||||
const 作品 = new Map()
|
const 作品 = new Map()
|
||||||
const 游戏 = new Map()
|
const 游戏 = new Map()
|
||||||
const 截图 = new Map()
|
const 截图 = new Map()
|
||||||
|
const 格式 = new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' })
|
||||||
|
|
||||||
|
|
||||||
|
var 日期 = 格式.format(new Date()).replace(/\//g, '-')
|
||||||
|
var 存档 = new Date()
|
||||||
|
|
||||||
|
if (fs.existsSync(`./data/${日期}.json`)) {
|
||||||
|
console.log(日期, '启动时读入数据..')
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log(日期, '开始收集日志..')
|
||||||
|
|
||||||
|
|
||||||
tail.on('line', (line) => {
|
tail.on('line', (line) => {
|
||||||
const item = JSON.parse(line)
|
const item = JSON.parse(line)
|
||||||
if (item.level !== 'debug') return
|
if (item.level !== 'debug') return
|
||||||
if (item.msg !== 'upstream roundtrip') return
|
if (item.msg !== 'upstream roundtrip') return
|
||||||
|
|
||||||
|
if ((new Date()).getTime() - 存档.getTime() > 600000) {
|
||||||
|
console.log('10 分钟存档一次..')
|
||||||
|
fs.promises.writeFile(`./data/${日期}.json`, JSON.stringify({
|
||||||
|
作品: Object.fromEntries(作品),
|
||||||
|
游戏: Object.fromEntries(游戏),
|
||||||
|
截图: Object.fromEntries(截图),
|
||||||
|
}, null, 2))
|
||||||
|
存档 = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
const 日志日期 = 格式.format(new Date(item.ts * 1000)).replace(/\//g, '-')
|
||||||
|
if (日期 !== 日志日期) {
|
||||||
|
fs.promises.writeFile(`./data/${日期} 作品.json`, JSON.stringify(Object.fromEntries(作品), null, 2))
|
||||||
|
fs.promises.writeFile(`./data/${日期} 游戏.json`, JSON.stringify(Object.fromEntries(游戏), null, 2))
|
||||||
|
fs.promises.writeFile(`./data/${日期} 截图.json`, JSON.stringify(Object.fromEntries(截图), null, 2))
|
||||||
|
日期 = 日志日期
|
||||||
|
}
|
||||||
|
|
||||||
if (item.request.uri.startsWith('/web/v1/article/get')) {
|
if (item.request.uri.startsWith('/web/v1/article/get')) {
|
||||||
const reg = /\/web\/v1\/article\/get\?id=(\d+)&userId=(\d+)/
|
const reg = /\/web\/v1\/article\/get\?id=(\d+)&userId=(\d+)/
|
||||||
const [uri, id, user_id] = item.request.uri.match(reg) ?? []
|
const [uri, id, user_id] = item.request.uri.match(reg) ?? []
|
||||||
if (uri && id && user_id) {
|
if (uri && id && user_id && item.request.headers.Referer) {
|
||||||
// 判断类型
|
if (item.request.headers.Referer[0].includes('/inspirationInfo/')) {
|
||||||
游戏.set(id, 游戏.has(id) ? 游戏.get(id) + 1 : 1)
|
游戏.set(id, 游戏.has(id) ? 游戏.get(id) + 1 : 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (item.request.headers.Referer[0].includes('/articleDetails/')) {
|
||||||
|
作品.set(id, 作品.has(id) ? 作品.get(id) + 1 : 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(item.request.headers.Referer)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -44,7 +91,15 @@ tail.on('error', (error) => {
|
|||||||
console.error('错误:', error)
|
console.error('错误:', error)
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', async () => {
|
||||||
tail.unwatch()
|
tail.unwatch()
|
||||||
|
|
||||||
|
console.log('退出前储存数据..')
|
||||||
|
await fs.promises.writeFile(`./data/${日期}.json`, JSON.stringify({
|
||||||
|
作品: Object.fromEntries(作品),
|
||||||
|
游戏: Object.fromEntries(游戏),
|
||||||
|
截图: Object.fromEntries(截图),
|
||||||
|
}, null, 2))
|
||||||
|
|
||||||
process.exit()
|
process.exit()
|
||||||
})
|
})
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
"author": "satori",
|
"author": "satori",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"express": "^4.21.1",
|
||||||
"tail": "^2.2.6"
|
"tail": "^2.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
server.js
Normal file
43
server.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import express from 'express'
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
app.use(express.json())
|
||||||
|
app.get('/api', (req, res) => {
|
||||||
|
const { start, end } = req.query
|
||||||
|
console.log(req.query)
|
||||||
|
if (!start || !end) return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: '请求无效,参数错误',
|
||||||
|
})
|
||||||
|
|
||||||
|
const 作品 = new Map()
|
||||||
|
const 游戏 = new Map()
|
||||||
|
const 截图 = new Map()
|
||||||
|
|
||||||
|
// 读取目录中的所有文件
|
||||||
|
fs.readdirSync('./data/').filter(name => {
|
||||||
|
const type = name.endsWith('.json')
|
||||||
|
const match = name.match(/\d{4}-\d{2}-\d{2}/)
|
||||||
|
if (!match) return false
|
||||||
|
const date = new Date(match[0])
|
||||||
|
return type && new Date(start) <= date && new Date(end) >= date
|
||||||
|
}).forEach(name => {
|
||||||
|
const file = fs.readFileSync(`./data/${name}`, 'utf8')
|
||||||
|
if (file) {
|
||||||
|
const data = JSON.parse(file)
|
||||||
|
Object.entries(data.作品).forEach(([k, v]) => 作品.set(k, v))
|
||||||
|
Object.entries(data.游戏).forEach(([k, v]) => 游戏.set(k, v))
|
||||||
|
Object.entries(data.截图).forEach(([k, v]) => 截图.set(k, v))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
作品: [...作品.entries()].sort((a, b) => b[1] - a[1]).map(([k]) => k),
|
||||||
|
游戏: [...游戏.entries()].sort((a, b) => b[1] - a[1]).map(([k]) => k),
|
||||||
|
截图: [...截图.entries()].sort((a, b) => b[1] - a[1]).map(([k]) => k),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(6005, () => console.log('http://localhost:6005'))
|
Reference in New Issue
Block a user