diff --git a/.gitignore b/.gitignore index 042f600..fc3697b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ access.log node_modules package-lock.json +data diff --git a/README.md b/README.md index 004af6e..79c0bb8 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,10 @@ 引入数据 创建指标 导出接口 + +```bash +npm install --registry=https://registry.npmmirror.com + +node main.js +``` + diff --git a/main.js b/main.js index d2e6011..be6f0ac 100644 --- a/main.js +++ b/main.js @@ -1,22 +1,69 @@ +import fs from 'fs' 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 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) => { const item = JSON.parse(line) if (item.level !== 'debug') 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')) { const reg = /\/web\/v1\/article\/get\?id=(\d+)&userId=(\d+)/ const [uri, id, user_id] = item.request.uri.match(reg) ?? [] - if (uri && id && user_id) { - // 判断类型 - 游戏.set(id, 游戏.has(id) ? 游戏.get(id) + 1 : 1) + 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) + 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 } @@ -44,7 +91,15 @@ tail.on('error', (error) => { console.error('错误:', error) }) -process.on('SIGINT', () => { +process.on('SIGINT', async () => { tail.unwatch() + + console.log('退出前储存数据..') + await fs.promises.writeFile(`./data/${日期}.json`, JSON.stringify({ + 作品: Object.fromEntries(作品), + 游戏: Object.fromEntries(游戏), + 截图: Object.fromEntries(截图), + }, null, 2)) + process.exit() }) diff --git a/package.json b/package.json index aa7169e..0bb57c4 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "author": "satori", "license": "GPL-3.0-only", "dependencies": { + "express": "^4.21.1", "tail": "^2.2.6" } } diff --git a/server.js b/server.js new file mode 100644 index 0000000..a9979b3 --- /dev/null +++ b/server.js @@ -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'))