import Log from '#models/log' import type { HttpContext } from '@adonisjs/core/http' import fs from 'node:fs' import path, { dirname } from 'node:path' import { fileURLToPath } from 'node:url' const FILENAME = fileURLToPath(import.meta.url) const DIRNAME = dirname(FILENAME) export default class LogsController { async list({ request, response, auth }: HttpContext) { const search = request.input('search', '') const perPage = request.input('per_page', 10) const page = request.input('page', 1) // Fetch logs that are associated with the user's stations with pagination const query = Log.query() .select('logs.*') .preload('line', (lineQuery) => { lineQuery.preload('station') }) const logs = await query.paginate(page, perPage) return response.ok({ status: true, data: logs, }) } async viewLog({ request, response }: HttpContext) { const logFilePath = request.input('path') try { const normalizedPath = path.normalize(logFilePath) const fullPath = path.join(DIRNAME, '..', '..', normalizedPath) if (!fs.existsSync(fullPath)) { return response.notFound({ status: false, message: 'Log file not found', }) } // Đọc file với encoding buffer trước const buffer = fs.readFileSync(fullPath) // Thử các encoding khác nhau let logContent try { // Thử với utf-8 trước logContent = buffer.toString('utf-8') if (logContent.includes('\u0000')) { // Nếu có null bytes, thử với encoding khác logContent = buffer.toString('ascii') } } catch { // Fallback to ascii if utf-8 fails logContent = buffer.toString('ascii') } // Loại bỏ các null bytes logContent = logContent.replace(/\u0000/g, '') return response.ok({ status: true, data: logContent, }) } catch (error) { return response.internalServerError({ status: false, message: 'Failed to read log file', error: error.message, }) } } async downloadLog({ request, response }: HttpContext) { try { const logPath = request.input('path') const fullPath = path.join(DIRNAME, '..', '..', logPath) if (!fs.existsSync(fullPath)) { return response.notFound({ status: false, message: 'Log file not found', }) } // Lấy tên file từ đường dẫn const fileName = path.basename(logPath) // Set headers cho download response.header('Content-Type', 'application/octet-stream') response.header('Content-Disposition', `attachment; filename="${fileName}"`) // Stream file về client return response.stream(fs.createReadStream(fullPath)) } catch (error) { return response.internalServerError({ status: false, message: 'Failed to download log file', error: error.message, }) } } async listSystemLogFiles({ request, response }: HttpContext) { try { const filename = request.input('filename', '') const fromDate = request.input('from_date', '') const toDate = request.input('to_date', '') const systemLogsDir = path.join(DIRNAME, '..', '..', 'storage', 'system_logs') // Nếu thư mục không tồn tại, trả về danh sách rỗng if (!fs.existsSync(systemLogsDir)) { return response.ok({ status: true, data: [], }) } let files = fs.readdirSync(systemLogsDir) // Lọc theo tên file nếu có if (filename) { files = files.filter((f) => f.includes(filename)) } // Lọc theo khoảng thời gian nếu có if (fromDate || toDate) { const fromTime = fromDate ? new Date(fromDate).getTime() : 0 // Cộng thêm 24h để bao gồm cả ngày cuối cùng (end of day 23:59:59) const toTime = toDate ? new Date(toDate).getTime() + 24 * 60 * 60 * 1000 : Date.now() files = files.filter((f) => { const filePath = path.join(systemLogsDir, f) const stat = fs.statSync(filePath) const fileTime = stat.mtime.getTime() return fileTime >= fromTime && fileTime < toTime }) } // Lấy 100 tên file đầu tiên từ danh sách lọc được const result = files.slice(0, 100) return response.ok({ status: true, data: result, }) } catch (error) { return response.internalServerError({ status: false, message: 'Failed to list system log files', error: error.message, }) } } }