UNPKG

frost-api-wrapper

Version:

A wrapper that allows you to use the websocket and API from G-Portal.

133 lines (112 loc) 3.89 kB
import { LogLevel, RCEEvent } from "../constants"; import { inspect } from "util"; import { type LoggerOptions } from "../types"; import fs from "fs"; import path from "path"; import { v4 as uuidv4 } from "uuid"; import RCEManager from "./RCEManager"; enum ConsoleColor { Reset = "\x1b[0m", FgRed = "\x1b[31m", FgGreen = "\x1b[32m", FgYellow = "\x1b[33m", FgCyan = "\x1b[36m", FgMagenta = "\x1b[35m", } interface LogType { prefix: string; emoji: string; color: string; } export default class Logger { private emitter: RCEManager; private level: LogLevel; private file: string | undefined; private static LOG_TYPES: Record<string, LogType> = { warn: { prefix: "[WARNING]", emoji: "⚠️", color: ConsoleColor.FgYellow }, info: { prefix: "[INFO]", emoji: "💬", color: ConsoleColor.FgCyan }, debug: { prefix: "[DEBUG]", emoji: "🔧", color: ConsoleColor.FgMagenta }, error: { prefix: "[ERROR]", emoji: "❌", color: ConsoleColor.FgRed }, }; public constructor(emitter: RCEManager, opts: LoggerOptions) { this.level = opts.logLevel ?? LogLevel.Info; this.file = opts.logFile; this.emitter = emitter; if (this.file) { this.validateFilePath(); } } private validateFilePath(): void { if (this.file && !fs.existsSync(this.file)) { const dir = path.dirname(this.file); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(this.file, ""); } } private handleFileSize(): void { if (this.file) { const stats = fs.statSync(this.file); if (stats.size > 300_000_000) { const archiveName = `${this.file}.${uuidv4()}.log`; fs.renameSync(this.file, archiveName); fs.writeFileSync(this.file, ""); } } } private async logToFileAsync(type: string, content: any): Promise<void> { if (this.file) { const stats = await fs.promises.stat(this.file).catch(() => null); if (stats && stats.size > 300_000_000) { const archiveName = `${this.file}.${uuidv4()}.log`; await fs.promises.rename(this.file, archiveName); await fs.promises.writeFile(this.file, ""); } const logMessage = typeof content === "string" ? `[${type.toUpperCase()}]: ${content}\n` : `[${type.toUpperCase()}]: ${inspect(content, { depth: 5 })}\n`; await fs.promises.appendFile(this.file, logMessage); } } private format(content: any): string { return typeof content === "string" ? content : inspect(content, { depth: 5 }); } private formatTimestamp(date: Date): string { return date.toLocaleTimeString([], { hour12: true }); } private log( level: LogLevel, type: string, content: any, logType: LogType ): void { this.handleFileSize(); this.logToFileAsync(type, content).catch(console.error); if (this.level !== LogLevel.None && level <= this.level) { const timestamp = this.formatTimestamp(new Date()); const formattedMessage = `\x1b[90m[${timestamp}]\x1b[0m ${logType.color}${logType.prefix}${ConsoleColor.Reset} ${logType.emoji} ${this.format(content)}`; console.log(formattedMessage); this.emitter.emit(RCEEvent.Log, { level, content: this.format(content) }); } } public logMessage(level: LogLevel, type: keyof typeof Logger.LOG_TYPES, content: any): void { const logType = Logger.LOG_TYPES[type]; this.log(level, type, content, logType); } public warn(content: any): void { this.logMessage(LogLevel.Warn, "warn", content); } public info(content: any): void { this.logMessage(LogLevel.Info, "info", content); } public debug(content: any): void { this.logMessage(LogLevel.Debug, "debug", content); } public error(content: any): void { this.logMessage(LogLevel.Error, "error", content); } }