1
0
forked from noxious/server

Moved more socket logic into socket manager for easier DX

This commit is contained in:
Dennis Postma 2024-12-30 02:39:31 +01:00
parent a5c941cbb0
commit ba12674e7c
4 changed files with 82 additions and 71 deletions

View File

@ -4,6 +4,7 @@ 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)
@ -19,8 +20,8 @@ export class ConsoleManager {
this.logReader = new LogReader(process.cwd())
}
public async boot(io: Server): Promise<void> {
this.io = io
public async boot(): Promise<void> {
this.io = SocketManager.getIO()
await this.registry.loadCommands()
this.logReader.start()

View File

@ -8,6 +8,7 @@ import config from '#application/config'
import Logger, { LoggerType } from '#application/logger'
import { getAppPath } from '#application/storage'
import { TSocket } from '#application/types'
import SocketManager from '#managers/socketManager'
class QueueManager {
private connection!: IORedis
@ -16,8 +17,8 @@ class QueueManager {
private io!: SocketServer
private logger = Logger.type(LoggerType.QUEUE)
public async boot(io: SocketServer) {
this.io = io
public async boot() {
this.io = SocketManager.getIO()
this.connection = new IORedis(config.REDIS_URL, {
maxRetriesPerRequest: null

View File

@ -1,27 +1,53 @@
import { Server as SocketServer } from 'socket.io'
import fs from 'fs'
import { pathToFileURL } from 'url'
import { Server as SocketServer } from 'socket.io'
import { Server as HTTPServer } from 'http'
import { Application } from 'express'
import Logger, { LoggerType } from '#application/logger'
import { getAppPath } from '#application/storage'
import { TSocket } from '#application/types'
import config from '#application/config'
import { Authentication } from '#middleware/authentication'
class SocketManager {
private io: any
private logger = Logger.type(LoggerType.APP)
private io: SocketServer | null = null
private readonly logger = Logger.type(LoggerType.APP)
public async boot(io: SocketServer) {
this.io = io
io.on('connection', this.handleConnection.bind(this))
/**
* Initialize Socket.IO server
*/
public async boot(app: Application, http: HTTPServer): Promise<void> {
this.io = new SocketServer(http, {
cors: {
origin: config.CLIENT_URL,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}
})
// Apply authentication middleware
this.io.use(Authentication)
// Set up connection handler
this.io.on('connection', this.handleConnection.bind(this))
}
/**
* Get Socket.IO server instance
*/
public getIO(): SocketServer {
if (!this.io) {
throw new Error('Socket.IO has not been initialized')
}
return this.io
}
/**
* Handle socket connection
* @param socket
* @private
*/
private async handleConnection(socket: TSocket) {
private async handleConnection(socket: TSocket): Promise<void> {
try {
await this.loadEventHandlers('events', '', socket)
} catch (error: any) {
@ -29,7 +55,10 @@ class SocketManager {
}
}
private async loadEventHandlers(baseDir: string, subDir: string, socket: TSocket) {
/**
* Load event handlers recursively from the events directory
*/
private async loadEventHandlers(baseDir: string, subDir: string, socket: TSocket): Promise<void> {
try {
const fullDir = getAppPath(baseDir, subDir)
const files = await fs.promises.readdir(fullDir, { withFileTypes: true })
@ -64,6 +93,20 @@ class SocketManager {
this.logger.error(`Error reading directory: ${error instanceof Error ? error.message : String(error)}`)
}
}
/**
* Emit event to all connected clients
*/
public emit(event: string, ...args: any[]): void {
this.getIO().emit(event, ...args)
}
/**
* Emit event to specific room
*/
public emitToRoom(room: string, event: string, ...args: any[]): void {
this.getIO().to(room).emit(event, ...args)
}
}
export default new SocketManager()
export default new SocketManager()

View File

@ -1,8 +1,6 @@
import { createServer as httpServer, Server as HTTPServer } from 'http'
import cors from 'cors'
import express, { Application } from 'express'
import { Server as SocketServer } from 'socket.io'
import cors from 'cors'
import config from '#application/config'
import Database from '#application/database'
@ -15,77 +13,45 @@ import SocketManager from '#managers/socketManager'
import UserManager from '#managers/userManager'
import WeatherManager from '#managers/weatherManager'
import ZoneManager from '#managers/zoneManager'
import { Authentication } from '#middleware/authentication'
export class Server {
private readonly app: Application
private readonly http: HTTPServer
private readonly io: SocketServer
private readonly logger = Logger.type(LoggerType.APP)
/**
* Creates an instance of GameServer.
*/
constructor() {
this.app = express()
this.app.use(
cors({
origin: config.CLIENT_URL,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Add supported methods
allowedHeaders: ['Content-Type', 'Authorization'], // Add allowed headers
credentials: true
})
)
this.app.use(cors())
this.app.use(express.json())
this.app.use(express.urlencoded({ extended: true }))
this.http = httpServer(this.app)
this.io = new SocketServer(this.http)
this.io.use(Authentication)
}
/**
* Start the server
*/
public async start() {
// Connect to database
public async start(): Promise<void> {
try {
// Initialize database
await Database.initialize()
} catch (error: any) {
this.logger.error(`Database connection failed: ${error.message}`)
process.exit(1) // Exit if database connection fails
}
// Start the server
try {
// Start HTTP server
this.http.listen(config.PORT, config.HOST)
this.logger.info(`Socket.IO running on port ${config.PORT}`)
this.logger.info(`Server running on port ${config.PORT}`)
// Initialize managers
await Promise.all([
HttpManager.boot(this.app),
SocketManager.boot(this.app, this.http),
QueueManager.boot(),
UserManager.boot(),
// DateManager.boot(SocketManager.getIO()),
// WeatherManager.boot(SocketManager.getIO()),
ZoneManager.boot(),
ConsoleManager.boot()
])
} catch (error: any) {
this.logger.error(`Socket.IO failed to start: ${error.message}`)
this.logger.error(`Server failed to start: ${error.message}`)
process.exit(1)
}
// Load HTTP manager
await HttpManager.boot(this.app)
// Load queue manager
await QueueManager.boot(this.io)
// Load user manager
await UserManager.boot()
// Load date manager
// await DateManager.boot(this.io)
// Load weather manager
// await WeatherManager.boot(this.io)
// Load zoneEditor manager
await ZoneManager.boot()
// Load console manager
await ConsoleManager.boot(this.io)
// Load socket manager
await SocketManager.boot(this.io)
}
}