import { Request, Response } from 'express'; import { DyFM_HttpCallType } from '@futdevpro/fsm-dynamo'; import { DyNTS_Controller } from '../../_services/route/controller.service'; import { DyNTS_Endpoint_Params } from '../../_models/control-models/endpoint-params.control-model'; import { DyNTS_FileLog_Entry } from './_models/file-log-entry.interface'; import { DyNTS_FileLog_ReadResult, DyNTS_FileLog_ReadOptions } from './_models/file-log-read-result.interface'; import { DyNTS_FileLog_Service } from './file-log.service'; /** * Config a `DyNTS_FileLogs_Controller`-hez (opcionalis auth pre-process). */ export interface DyNTS_FileLogsController_Config { /** * Opcionalis auth pre-process — ha megadod, a controller-en levo endpointok * vedettek. Tipikusan: `DyNTS_AdminApiKey_AuthService.getInstance().verify`. */ authPreProcess?: (req: Request, res: Response) => Promise; } /** * NTS File Logs Controller — endpointok a `DyNTS_FileLog_Service` altal irt * log fajl-ok lekeresehez. * * **Endpoint-ok:** * GET /list — log fajlok listazasa (basename, sizeBytes, mtimeMs, isCurrent) * GET /file/:filename — egy fajl olvasasa, line-based: ?tail=N | ?head=N | ?rangeStart=A&rangeEnd=B * * **Mount-olas (host app):** * app.use(DyNTS_getFileLogsRoutingModule({ * authPreProcess: adminAuth.verify, // opcionalis * route: '/admin/file-logs', // default '/file-logs' * })); * * **Status code-ok:** * 200 — sikeres response (lehet ures lista is) * 400 — invalid query parameter (tail/head/range) * 401 — auth pre-process throw-ol DyFM_Error(401) * 404 — fajl nem letezik vagy nem megfelelo nev (whitelist failure / path traversal kiserlet) * 503 — a DyNTS_FileLog_Service nincs install-olva (vagy file_log.enabled=false) */ export class DyNTS_FileLogs_Controller extends DyNTS_Controller { private static config: DyNTS_FileLogsController_Config = {}; static configure(config: DyNTS_FileLogsController_Config): void { DyNTS_FileLogs_Controller.config = config; } static getInstance(): DyNTS_FileLogs_Controller { return DyNTS_FileLogs_Controller.getSingletonInstance(); } private readonly fileLogService: DyNTS_FileLog_Service = DyNTS_FileLog_Service.getInstance(); setupEndpoints(): void { const preProcesses: ((req: Request, res: Response) => Promise)[] = []; if (DyNTS_FileLogs_Controller.config.authPreProcess) { preProcesses.push(DyNTS_FileLogs_Controller.config.authPreProcess); } this.endpoints = [ // GET /list — fajllista new DyNTS_Endpoint_Params({ name: 'listFileLogs', type: DyFM_HttpCallType.get, endpoint: '/list', preProcesses: preProcesses, logRequest: false, tasks: [ async (req: Request, res: Response): Promise => { if (!this.fileLogService.isInstalled()) { res.status(503).send({ error: 'FileLog service is not installed.', hint: 'Set DyNTS_global_settings.log_settings.file_log.enabled = true and call DyNTS_FileLog_Service.getInstance().install() at server startup.', }); return; } const files: DyNTS_FileLog_Entry[] = this.fileLogService.listLogFiles(); const totalSizeBytes: number = files.reduce( (acc: number, f: DyNTS_FileLog_Entry): number => acc + f.sizeBytes, 0, ); res.send({ count: files.length, totalSizeBytes: totalSizeBytes, files: files, }); }, ], }), // GET /file/:filename — egy fajl olvasasa tail / head / range modban new DyNTS_Endpoint_Params({ name: 'readFileLog', type: DyFM_HttpCallType.get, endpoint: '/file/:filename', preProcesses: preProcesses, logRequest: false, tasks: [ async (req: Request, res: Response): Promise => { if (!this.fileLogService.isInstalled()) { res.status(503).send({ error: 'FileLog service is not installed.', }); return; } const filename: string = req.params.filename; const options: DyNTS_FileLog_ReadOptions = {}; // Mod prioritas: range > head > tail. Csak a megadott mezo aktivalja az adott modot. if (req.query.rangeStart !== undefined && req.query.rangeEnd !== undefined) { const start: number = parseInt(req.query.rangeStart as string, 10); const end: number = parseInt(req.query.rangeEnd as string, 10); if (!Number.isFinite(start) || !Number.isFinite(end) || start < 1 || end < start) { res.status(400).send({ error: 'Invalid rangeStart/rangeEnd query parameters' }); return; } options.rangeStart = start; options.rangeEnd = end; } else if (req.query.head !== undefined) { const head: number = parseInt(req.query.head as string, 10); if (!Number.isFinite(head) || head < 1) { res.status(400).send({ error: 'Invalid head query parameter' }); return; } options.head = head; } else if (req.query.tail !== undefined) { const tail: number = parseInt(req.query.tail as string, 10); if (!Number.isFinite(tail) || tail < 1) { res.status(400).send({ error: 'Invalid tail query parameter' }); return; } options.tail = tail; } // egyik query mezo sincs → service alapertelmezett tail (200) try { const result: DyNTS_FileLog_ReadResult = this.fileLogService.readLogFile(filename, options); res.send(result); } catch (err: unknown) { const msg: string = err instanceof Error ? err.message : String(err); if (msg === 'FileLog service not installed') { res.status(503).send({ error: msg }); return; } if (msg === 'invalid filename' || msg === 'file not found') { res.status(404).send({ error: msg, filename: filename }); return; } // Egyeb hibak (pl. FS read failure) — 500 res.status(500).send({ error: msg }); } }, ], }), ]; } }