new file: src/pages/api/log.ts new file: src/pages/log.tsx modified: tsconfig.jsonmaster
parent
04ca57a2aa
commit
021680325e
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@ |
|||||||
|
import fs from 'fs'; |
||||||
|
import type { NextApiRequest, NextApiResponse } from 'next'; |
||||||
|
|
||||||
|
// Путь к лог-файлу доступа Nginx
|
||||||
|
const logFilePath = '@../../log/access_blogbaster.log'; |
||||||
|
|
||||||
|
let log: string[] = []; |
||||||
|
|
||||||
|
export default function handler( |
||||||
|
req: NextApiRequest, |
||||||
|
res: NextApiResponse |
||||||
|
) { |
||||||
|
// Чтение лог-файла
|
||||||
|
fs.readFile(logFilePath, 'utf8', (err, data) => { |
||||||
|
if (err) throw err; |
||||||
|
|
||||||
|
// Разбиваем содержимое файла на строки
|
||||||
|
const logLines = data.split('\n'); |
||||||
|
|
||||||
|
// Объект для хранения статистики
|
||||||
|
const mp3Stats: { [key: string]: number } = {}; |
||||||
|
|
||||||
|
// Анализируем каждую строку лога
|
||||||
|
logLines.forEach((line) => { |
||||||
|
// Проверяем, содержит ли строка запрос к mp3 файлу
|
||||||
|
if (line.includes('.mp3')) { |
||||||
|
const fileName = line.match(/\/([^/]+\.mp3)/)?.[1]; |
||||||
|
if (fileName) { |
||||||
|
if (mp3Stats[fileName]) { |
||||||
|
mp3Stats[fileName]++; |
||||||
|
} else { |
||||||
|
mp3Stats[fileName] = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// Выводим статистику
|
||||||
|
for (const fileName in mp3Stats) { |
||||||
|
log.push(fileName + ',' + mp3Stats[fileName]); |
||||||
|
} |
||||||
|
//let logData = JSON.stringify(log);
|
||||||
|
res.status(200).json(log); |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
import React, { useState, useEffect } from 'react'; |
||||||
|
|
||||||
|
type LogData = { |
||||||
|
fileName: string; |
||||||
|
accessCount: number; |
||||||
|
}; |
||||||
|
|
||||||
|
const LogTable: React.FC = () => { |
||||||
|
const [logData, setLogData] = useState<LogData[]>([]); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
fetch('/api/log') // Обращаемся к REST API
|
||||||
|
.then((response) => response.json()) |
||||||
|
.then((data: string[]) => { |
||||||
|
const parsedData: LogData[] = data.map((item: string) => { |
||||||
|
const [fileName, accessCountStr] = item.split(","); |
||||||
|
const accessCount: number = parseInt(accessCountStr); |
||||||
|
return { fileName, accessCount }; |
||||||
|
}); |
||||||
|
setLogData(parsedData); |
||||||
|
}); |
||||||
|
}, []); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="m-3 card lg:card-side bg-base-100 shadow-xl"> |
||||||
|
<div className="flex flex-col items-center bg-white border border-gray-200 rounded-lg shadow md:flex-row "> |
||||||
|
<div className="mb-3 object-cover rounded-t-lg md:w-auto md:min-w-64 md:max-w-64 md:rounded-s-lg md:m-3"> |
||||||
|
<div> |
||||||
|
<h2>Статистика доступа к mp3 файлам</h2> |
||||||
|
<div className="container mx-auto p-6"> |
||||||
|
<table className="min-w-full bg-white border border-gray-300"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th className="py-2 px-4 border-b">№</th> |
||||||
|
<th className="py-2 px-4 border-b">Имя файла</th> |
||||||
|
<th className="py-2 px-4 border-b">Количество доступов</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{logData.map((item, index) => ( |
||||||
|
<tr key={index}> |
||||||
|
<td className="py-2 px-4 border-b">{index + 1}</td> |
||||||
|
<td className="py-2 px-4 border-b">{item.fileName}</td> |
||||||
|
<td className="py-2 px-4 border-b">{item.accessCount}</td> |
||||||
|
</tr> |
||||||
|
))} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default LogTable; |
Loading…
Reference in new issue