diff --git a/package.json b/package.json index 1625d49..61290b4 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "helmet": "^7.1.0", "litemcp": "^0.7.0", "uuid": "^9.0.1", + "winston-daily-rotate-file": "^5.0.0", "ws": "^8.16.0", "zod": "^3.22.4" }, @@ -46,11 +47,12 @@ "@types/node": "^20.17.16", "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.8", + "@types/winston": "^2.4.4", "@types/ws": "^8.5.10", "jest": "^29.7.0", "node-fetch": "^3.3.2", "openai": "^4.82.0", - "rimraf": "^5.0.5", + "rimraf": "^5.0.10", "supertest": "^6.3.4", "ts-jest": "^29.1.2", "tsx": "^4.7.0", diff --git a/src/api/routes.ts b/src/api/routes.ts index 1f6ebc3..7ab2170 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -4,7 +4,7 @@ import { middleware } from '../middleware/index.js'; import { sseManager } from '../sse/index.js'; import { v4 as uuidv4 } from 'uuid'; import { TokenManager } from '../security/index.js'; -import { tools } from '../services/tools.js'; +import { tools } from '../tools/index.js'; import { Tool } from '../interfaces/index.js'; const router = Router(); diff --git a/src/routes/index.ts b/src/routes/index.ts index 7e88121..19baca3 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -9,10 +9,10 @@ */ import { Router } from 'express'; -import { mcpRoutes } from './mcp.routes'; -import { sseRoutes } from './sse.routes'; -import { toolRoutes } from './tool.routes'; -import { healthRoutes } from './health.routes'; +import { mcpRoutes } from './mcp.routes.js'; +import { sseRoutes } from './sse.routes.js'; +import { toolRoutes } from './tool.routes.js'; +import { healthRoutes } from './health.routes.js'; /** * Create main router instance diff --git a/src/routes/sse.routes.ts b/src/routes/sse.routes.ts index d0936e7..342892b 100644 --- a/src/routes/sse.routes.ts +++ b/src/routes/sse.routes.ts @@ -86,7 +86,7 @@ router.get('/subscribe', (req, res) => { // Get SSE stats endpoint router.get('/stats', async (req, res) => { try { - const stats = await sseManager.getStats(); + const stats = await sseManager.getStatistics(); res.json(stats); } catch (error) { res.status(500).json({ diff --git a/src/tools/index.ts b/src/tools/index.ts index a58694f..6b4c75f 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,15 +1,15 @@ -import { Tool } from '../types/index'; -import { listDevicesTool } from './list-devices.tool'; -import { controlTool } from './control.tool'; -import { historyTool } from './history.tool'; -import { sceneTool } from './scene.tool'; -import { notifyTool } from './notify.tool'; -import { automationTool } from './automation.tool'; -import { addonTool } from './addon.tool'; -import { packageTool } from './package.tool'; -import { automationConfigTool } from './automation-config.tool'; -import { subscribeEventsTool } from './subscribe-events.tool'; -import { getSSEStatsTool } from './sse-stats.tool'; +import { Tool } from '../types/index.js'; +import { listDevicesTool } from './list-devices.tool.js'; +import { controlTool } from './control.tool.js'; +import { historyTool } from './history.tool.js'; +import { sceneTool } from './scene.tool.js'; +import { notifyTool } from './notify.tool.js'; +import { automationTool } from './automation.tool.js'; +import { addonTool } from './addon.tool.js'; +import { packageTool } from './package.tool.js'; +import { automationConfigTool } from './automation-config.tool.js'; +import { subscribeEventsTool } from './subscribe-events.tool.js'; +import { getSSEStatsTool } from './sse-stats.tool.js'; // Tool category types export enum ToolCategory { @@ -36,7 +36,7 @@ interface ToolMetadata { } // Array to track all tools -const tools: Tool[] = [ +export const tools: Tool[] = [ listDevicesTool, controlTool, historyTool, diff --git a/src/utils/log-rotation.ts b/src/utils/log-rotation.ts index 06665c2..d18a921 100644 --- a/src/utils/log-rotation.ts +++ b/src/utils/log-rotation.ts @@ -9,9 +9,14 @@ import fs from 'fs/promises'; import path from 'path'; -import { glob } from 'glob'; +import glob from 'glob'; import { logger } from './logger.js'; import { APP_CONFIG } from '../config/app.config.js'; +import { unlink } from 'fs/promises'; +import { join } from 'path'; +import { promisify } from 'util'; + +const globPromise = promisify(glob); /** * Interface for log file information @@ -68,7 +73,7 @@ const parseDuration = (duration: string): number => { */ const getLogFiles = async (): Promise => { const logDir = APP_CONFIG.LOGGING.DIR; - const files = await glob('*.log*', { cwd: logDir }); + const files = await globPromise('*.log*', { cwd: logDir }); const fileInfos: LogFileInfo[] = []; for (const file of files) { @@ -92,22 +97,32 @@ const getLogFiles = async (): Promise => { /** * Clean up old log files */ -const cleanupOldLogs = async (): Promise => { +export async function cleanupOldLogs(logDir: string, maxDays: number): Promise { try { - const maxDays = parseDuration(APP_CONFIG.LOGGING.MAX_DAYS); - const maxAge = Date.now() - (maxDays * 24 * 60 * 60 * 1000); + const files = await new Promise((resolve, reject) => { + glob('*.log*', { cwd: logDir }, (err, matches) => { + if (err) reject(err); + else resolve(matches); + }); + }); - const files = await getLogFiles(); - const oldFiles = files.filter(file => file.date.getTime() < maxAge); + const now = Date.now(); + const maxAge = maxDays * 24 * 60 * 60 * 1000; - for (const file of oldFiles) { - await fs.unlink(file.path); - logger.debug(`Deleted old log file: ${file.filename}`); + for (const file of files) { + const filePath = join(logDir, file); + const stats = await fs.stat(filePath); + const dateMatch = file.match(/\d{4}-\d{2}-\d{2}/); + + if (dateMatch && stats.ctimeMs < now - maxAge) { + await unlink(filePath); + logger.debug(`Deleted old log file: ${file}`); + } } } catch (error) { logger.error('Error cleaning up old logs:', error); } -}; +} /** * Check and rotate log files based on size @@ -147,7 +162,7 @@ export const initLogRotation = (): void => { }); // Initial cleanup - cleanupOldLogs().catch(error => { + cleanupOldLogs(APP_CONFIG.LOGGING.DIR, parseDuration(APP_CONFIG.LOGGING.MAX_DAYS)).catch(error => { logger.error('Error in initial log cleanup:', error); }); diff --git a/src/utils/logger.ts b/src/utils/logger.ts index ea8ae48..155e974 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -8,7 +8,7 @@ */ import winston from 'winston'; -import 'winston-daily-rotate-file'; +import DailyRotateFile from 'winston-daily-rotate-file'; import { APP_CONFIG } from '../config/app.config.js'; /** @@ -56,7 +56,7 @@ const format = winston.format.combine( * Transport for daily rotating file * Configures how logs are rotated and stored */ -const dailyRotateFileTransport = new winston.transports.DailyRotateFile({ +const dailyRotateFileTransport = new DailyRotateFile({ filename: 'logs/%DATE%.log', datePattern: 'YYYY-MM-DD', zippedArchive: true, @@ -73,7 +73,7 @@ const dailyRotateFileTransport = new winston.transports.DailyRotateFile({ * Transport for error logs * Stores error logs in a separate file */ -const errorFileTransport = new winston.transports.DailyRotateFile({ +const errorFileTransport = new DailyRotateFile({ filename: 'logs/error-%DATE%.log', datePattern: 'YYYY-MM-DD', level: 'error',