diff --git a/mikro-orm.config.ts b/mikro-orm.config.ts index 9f5ded4..a5d68eb 100644 --- a/mikro-orm.config.ts +++ b/mikro-orm.config.ts @@ -16,7 +16,7 @@ export default defineConfig({ user: serverConfig.DB_USER, password: serverConfig.DB_PASS, dbName: serverConfig.DB_NAME, - debug: serverConfig.ENV !== 'production', + // debug: serverConfig.ENV !== 'production', driverOptions: { allowPublicKeyRetrieval: true }, diff --git a/package-lock.json b/package-lock.json index 8ab468a..b1a8a80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,10 @@ "@mikro-orm/mysql": "^6.4.2", "@mikro-orm/reflection": "^6.4.2", "@prisma/client": "^6.1.0", + "@types/blessed": "^0.1.25", "@types/ioredis": "^4.28.10", "bcryptjs": "^2.4.3", + "blessed": "^0.1.81", "bullmq": "^5.13.2", "cors": "^2.8.5", "dotenv": "^16.4.5", @@ -1661,6 +1663,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/blessed": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.25.tgz", + "integrity": "sha512-kQsjBgtsbJLmG6CJA+Z6Nujj+tq1fcSE3UIowbDvzQI4wWmoTV7djUDhSo5lDjgwpIN0oRvks0SA5mMdKE5eFg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -2390,6 +2401,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", + "license": "MIT", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", diff --git a/package.json b/package.json index 5e39860..391d826 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "useTsNode": true, "alwaysAllowTs": true, "scripts": { - "start": "npx prisma migrate deploy && node dist/server.js", - "dev": "nodemon --ignore 'data/*' --exec tsx src/server.ts", + "start": "node dist/server.js", + "dev": "nodemon --exec tsx src/server.ts", "build": "tsc", "format": "prettier --write src/", "lint": "eslint .", @@ -16,8 +16,10 @@ "@mikro-orm/mysql": "^6.4.2", "@mikro-orm/reflection": "^6.4.2", "@prisma/client": "^6.1.0", + "@types/blessed": "^0.1.25", "@types/ioredis": "^4.28.10", "bcryptjs": "^2.4.3", + "blessed": "^0.1.81", "bullmq": "^5.13.2", "cors": "^2.8.5", "dotenv": "^16.4.5", diff --git a/src/application/console/commandRegistry.ts b/src/application/console/commandRegistry.ts deleted file mode 100644 index 2faeee8..0000000 --- a/src/application/console/commandRegistry.ts +++ /dev/null @@ -1,57 +0,0 @@ -import * as fs from 'fs' -import * as path from 'path' -import { pathToFileURL } from 'url' - -import Logger, { LoggerType } from '#application/logger' -import Storage from '#application/storage' -import { Command } from '#application/types' - -export class CommandRegistry { - private readonly commands: Map = new Map() - private readonly logger = Logger.type(LoggerType.COMMAND) - - public getCommand(name: string): Command | undefined { - return this.commands.get(name) - } - - public async loadCommands(): Promise { - const directory = Storage.getAppPath('commands') - this.logger.info(`Loading commands from: ${directory}`) - - try { - const files = await fs.promises.readdir(directory, { withFileTypes: true }) - await Promise.all(files.filter((file) => this.isValidCommandFile(file)).map((file) => this.loadCommandFile(file))) - } catch (error) { - this.logger.error(`Failed to read commands directory: ${error instanceof Error ? error.message : String(error)}`) - } - } - - private isValidCommandFile(file: fs.Dirent): boolean { - return file.isFile() && (file.name.endsWith('.ts') || file.name.endsWith('.js')) - } - - private async loadCommandFile(file: fs.Dirent): Promise { - try { - const filePath = Storage.getAppPath('commands', file.name) - const commandName = path.basename(file.name, path.extname(file.name)) - - const module = await import(pathToFileURL(filePath).href) - if (typeof module.default !== 'function') { - this.logger.warn(`Unrecognized export in ${file.name}`) - return - } - - this.registerCommand(commandName, module.default) - } catch (error) { - this.logger.error(`Error loading command ${file.name}: ${error instanceof Error ? error.message : String(error)}`) - } - } - - private registerCommand(name: string, CommandClass: Command): void { - if (this.commands.has(name)) { - this.logger.warn(`Command '${name}' is already registered. Overwriting...`) - } - this.commands.set(name, CommandClass) - this.logger.info(`Registered command: ${name}`) - } -} diff --git a/src/application/console/consolePrompt.ts b/src/application/console/consolePrompt.ts deleted file mode 100644 index 41aaa74..0000000 --- a/src/application/console/consolePrompt.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as readline from 'readline' - -export class ConsolePrompt { - private readonly rl: readline.Interface - private isClosed: boolean = false - - constructor(private readonly commandHandler: (command: string) => void) { - this.rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }) - - this.rl.on('close', () => { - this.isClosed = true - }) - } - - public start(): void { - if (this.isClosed) return - this.promptCommand() - } - - public close(): void { - this.rl.close() - } - - private promptCommand(): void { - this.rl.question('> ', (command: string) => { - this.commandHandler(command) - this.promptCommand() - }) - } -} diff --git a/src/application/console/logReader.ts b/src/application/console/logReader.ts deleted file mode 100644 index 6385bc2..0000000 --- a/src/application/console/logReader.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as fs from 'fs' -import * as path from 'path' - -import Logger, { LoggerType } from '#application/logger' - -export class LogReader { - private logger = Logger.type(LoggerType.CONSOLE) - private watchers: fs.FSWatcher[] = [] - private readonly logsDirectory: string - - constructor(rootPath: string) { - this.logsDirectory = path.join(rootPath, 'logs') - } - - public start(): void { - this.logger.info('Starting log reader...') - this.watchLogs() - } - - public stop(): void { - this.watchers.forEach((watcher) => watcher.close()) - this.watchers = [] - } - - private watchLogs(): void { - // Watch directory for new files - const directoryWatcher = fs.watch(this.logsDirectory, (_, filename) => { - if (filename?.endsWith('.log')) { - this.watchLogFile(filename) - } - }) - this.watchers.push(directoryWatcher) - - // Watch existing files - try { - fs.readdirSync(this.logsDirectory) - .filter((file) => file.endsWith('.log')) - .forEach((file) => this.watchLogFile(file)) - } catch (error) { - this.logger.error(`Error reading logs directory: ${error}`) - } - } - - private watchLogFile(filename: string): void { - const filePath = path.join(this.logsDirectory, filename) - let currentPosition = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0 - - const watcher = fs.watch(filePath, () => { - try { - const stat = fs.statSync(filePath) - const newPosition = stat.size - - if (newPosition < currentPosition) { - currentPosition = 0 - } - - if (newPosition > currentPosition) { - const stream = fs.createReadStream(filePath, { - start: currentPosition, - end: newPosition - }) - - stream.on('data', (data) => { - process.stdout.write('\r' + `[${filename}]\n${data}`) - process.stdout.write('\n> ') - }) - - currentPosition = newPosition - } - } catch { - watcher.close() - } - }) - - this.watchers.push(watcher) - } -} diff --git a/src/managers/consoleManager.ts b/src/managers/consoleManager.ts index 1d71106..14378c5 100644 --- a/src/managers/consoleManager.ts +++ b/src/managers/consoleManager.ts @@ -1,53 +1,8 @@ -import { Server } from 'socket.io' -import { CommandRegistry } from '#application/console/commandRegistry' -import { ConsolePrompt } from '#application/console/consolePrompt' -import { LogReader } from '#application/console/logReader' -import Logger, { LoggerType } from '#application/logger' -import SocketManager from '#managers/socketManager' - -export class ConsoleManager { - private readonly logger = Logger.type(LoggerType.COMMAND) - private readonly registry: CommandRegistry - private readonly prompt: ConsolePrompt - private readonly logReader: LogReader - - constructor() { - this.registry = new CommandRegistry() - this.prompt = new ConsolePrompt((command: string) => this.processCommand(command)) - - this.logReader = new LogReader(process.cwd()) - } - - public async boot(): Promise { - await this.registry.loadCommands() - this.logReader.start() - this.prompt.start() - - this.logger.info('Console manager loaded') - } - - private async processCommand(commandLine: string): Promise { - const [cmd, ...args] = commandLine.trim().split(' ') - - if (cmd === 'exit') { - this.prompt.close() - return - } - - const CommandClass = this.registry.getCommand(cmd) - if (!CommandClass) { - console.error(`Unknown command: ${cmd}`) - return - } - - try { - const commandInstance = new CommandClass(SocketManager.getIO()) - await commandInstance.execute(args) - } catch (error) { - this.logger.error(`Error executing command ${cmd}: ${error instanceof Error ? error.message : String(error)}`) - } +class ConsoleManager { + async boot() { + console.log('Console manager loaded') } } -export default new ConsoleManager() +export default new ConsoleManager() \ No newline at end of file