mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-16 04:02:54 -06:00

- Add robust authentication mechanism for WebSocket connections - Implement API key validation for both instance-specific and global tokens - Improve connection request handling with detailed logging - Refactor WebSocket controller to support more secure connection validation
161 lines
4.5 KiB
TypeScript
161 lines
4.5 KiB
TypeScript
import { PrismaRepository } from '@api/repository/repository.service';
|
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
|
import { Auth, configService, Cors, Log, Websocket } from '@config/env.config';
|
|
import { Logger } from '@config/logger.config';
|
|
import { Server } from 'http';
|
|
import { Server as SocketIO } from 'socket.io';
|
|
|
|
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
|
|
|
export class WebsocketController extends EventController implements EventControllerInterface {
|
|
private io: SocketIO;
|
|
private corsConfig: Array<any>;
|
|
private readonly logger = new Logger('WebsocketController');
|
|
|
|
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
|
super(prismaRepository, waMonitor, configService.get<Websocket>('WEBSOCKET')?.ENABLED, 'websocket');
|
|
|
|
this.cors = configService.get<Cors>('CORS').ORIGIN;
|
|
}
|
|
|
|
public init(httpServer: Server): void {
|
|
if (!this.status) {
|
|
return;
|
|
}
|
|
|
|
this.socket = new SocketIO(httpServer, {
|
|
cors: { origin: this.cors },
|
|
allowRequest: async (req, callback) => {
|
|
try {
|
|
const url = new URL(req.url || '', 'http://localhost');
|
|
const params = new URLSearchParams(url.search);
|
|
|
|
// Permite conexões internas do Socket.IO (EIO=4 é o Engine.IO v4)
|
|
if (params.has('EIO')) {
|
|
return callback(null, true);
|
|
}
|
|
|
|
const apiKey = params.get('apikey') || (req.headers.apikey as string);
|
|
|
|
if (!apiKey) {
|
|
this.logger.error('Connection rejected: apiKey not provided');
|
|
return callback('apiKey is required', false);
|
|
}
|
|
|
|
const instance = await this.prismaRepository.instance.findFirst({ where: { token: apiKey } });
|
|
|
|
if (!instance) {
|
|
const globalToken = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
|
|
if (apiKey !== globalToken) {
|
|
this.logger.error('Connection rejected: invalid global token');
|
|
return callback('Invalid global token', false);
|
|
}
|
|
}
|
|
|
|
callback(null, true);
|
|
} catch (error) {
|
|
this.logger.error('Authentication error:');
|
|
this.logger.error(error);
|
|
callback('Authentication error', false);
|
|
}
|
|
},
|
|
});
|
|
|
|
this.socket.on('connection', (socket) => {
|
|
this.logger.info('User connected');
|
|
|
|
socket.on('disconnect', () => {
|
|
this.logger.info('User disconnected');
|
|
});
|
|
|
|
socket.on('sendNode', async (data) => {
|
|
try {
|
|
await this.waMonitor.waInstances[data.instanceId].baileysSendNode(data.stanza);
|
|
this.logger.info('Node sent successfully');
|
|
} catch (error) {
|
|
this.logger.error('Error sending node:');
|
|
this.logger.error(error);
|
|
}
|
|
});
|
|
});
|
|
|
|
this.logger.info('Socket.io initialized');
|
|
}
|
|
|
|
private set cors(cors: Array<any>) {
|
|
this.corsConfig = cors;
|
|
}
|
|
|
|
private get cors(): string | Array<any> {
|
|
return this.corsConfig?.includes('*') ? '*' : this.corsConfig;
|
|
}
|
|
|
|
private set socket(socket: SocketIO) {
|
|
this.io = socket;
|
|
}
|
|
|
|
public get socket(): SocketIO {
|
|
return this.io;
|
|
}
|
|
|
|
public async emit({
|
|
instanceName,
|
|
origin,
|
|
event,
|
|
data,
|
|
serverUrl,
|
|
dateTime,
|
|
sender,
|
|
apiKey,
|
|
integration,
|
|
}: EmitData): Promise<void> {
|
|
if (integration && !integration.includes('websocket')) {
|
|
return;
|
|
}
|
|
|
|
if (!this.status) {
|
|
return;
|
|
}
|
|
|
|
const configEv = event.replace(/[.-]/gm, '_').toUpperCase();
|
|
const logEnabled = configService.get<Log>('LOG').LEVEL.includes('WEBSOCKET');
|
|
const message = {
|
|
event,
|
|
instance: instanceName,
|
|
data,
|
|
server_url: serverUrl,
|
|
date_time: dateTime,
|
|
sender,
|
|
apikey: apiKey,
|
|
};
|
|
|
|
if (configService.get<Websocket>('WEBSOCKET')?.GLOBAL_EVENTS) {
|
|
this.socket.emit(event, message);
|
|
|
|
if (logEnabled) {
|
|
this.logger.log({ local: `${origin}.sendData-WebsocketGlobal`, ...message });
|
|
}
|
|
}
|
|
|
|
try {
|
|
const instance = await this.get(instanceName);
|
|
|
|
if (!instance?.enabled) {
|
|
return;
|
|
}
|
|
|
|
if (Array.isArray(instance?.events) && instance?.events.includes(configEv)) {
|
|
this.socket.of(`/${instanceName}`).emit(event, message);
|
|
|
|
if (logEnabled) {
|
|
this.logger.log({ local: `${origin}.sendData-Websocket`, ...message });
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (logEnabled) {
|
|
this.logger.log(err);
|
|
}
|
|
}
|
|
}
|
|
}
|