primer commit

This commit is contained in:
2026-01-17 21:43:47 -06:00
commit c8450ed5a8
40 changed files with 11170 additions and 0 deletions

293
gateway/src/index.ts Normal file
View File

@@ -0,0 +1,293 @@
import express from 'express';
//import cors from 'cors';
import dotenv from 'dotenv';
import { WhatsAppClient } from './whatsapp/client';
import { QRSocketServer } from './sockets/qr.socket';
import { SendController } from './api/send';
import { StatusController } from './api/status';
import { SessionController } from './api/session';
import { MessagesController } from './api/messages';
import { n8nController } from './api/n8n';
import { WebhookController } from './api/webhook';
import { AuthMiddleware } from './middleware/auth';
import { logger } from './config/logger';
dotenv.config();
class WhatsAppGateway {
private app: express.Application;
private whatsappClient!: WhatsAppClient;
private socketServer!: QRSocketServer;
private sendController!: SendController;
private statusController!: StatusController;
private sessionController!: SessionController;
private messagesController!: MessagesController;
private n8nController!: n8nController;
private webhookController!: WebhookController;
constructor() {
this.app = express();
this.setupMiddleware();
this.initializeComponents();
this.setupRoutes();
}
private setupMiddleware(): void {
// Configure CORS for n8n and Manager
const corsOrigins = [
...(process.env.CORS_ORIGIN ? process.env.CORS_ORIGIN.split(',') : ['http://localhost:3002']),
'http://localhost:3004', // Add 3004 for current setup
'http://localhost:5678', // n8n default
'http://localhost:5679', // n8n alternative
...(process.env.N8N_ORIGINS ? process.env.N8N_ORIGINS.split(',') : [])
];
// ⚠️ SOLUCIÓN BRUTA — SOLO DEV
this.app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Intercepta preflight
if (req.method === 'OPTIONS') {
return res.sendStatus(204);
}
next();
});
/* this.app.use(cors({
origin: corsOrigins,
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));*/
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true }));
}
private initializeComponents(): void {
const sessionId = process.env.SESSION_ID || 'default';
this.whatsappClient = new WhatsAppClient(sessionId);
this.socketServer = new QRSocketServer(3003);
this.sendController = new SendController(this.whatsappClient);
this.statusController = new StatusController(this.whatsappClient);
this.sessionController = new SessionController(this.whatsappClient);
this.messagesController = new MessagesController();
this.n8nController = new n8nController(this.whatsappClient);
this.webhookController = new WebhookController(process.env.WEBHOOK_URL);
this.setupWhatsAppEvents();
this.connectWhatsApp();
this.setupWebSocketCommands();
// Send current state when new clients connect
this.socketServer.onClientConnect = () => {
const currentState = this.whatsappClient.getConnectionState();
this.socketServer.sendStatus(currentState);
};
}
private setupWebSocketCommands(): void {
// Handle WebSocket commands from Manager
this.socketServer.onCommand = async (command: string, data?: any) => {
logger.info(`Received WebSocket command: ${command}`);
switch (command) {
case 'restart_session':
await this.sessionController.restartSession({} as any, { json: () => {} } as any);
break;
case 'logout_session':
await this.sessionController.logoutSession({} as any, { json: () => {} } as any);
break;
case 'generate_token':
const tokenResponse = await this.sessionController.generateToken({} as any, { json: (data: any) => {
this.socketServer.broadcast({
type: 'token_generated',
data: JSON.stringify(data)
});
} } as any);
break;
case 'get_recent_messages':
await this.messagesController.getRecentMessages({} as any, { json: (data: any) => {
this.socketServer.broadcast({
type: 'status',
data: JSON.stringify(data)
});
} } as any);
break;
default:
logger.warn(`Unknown WebSocket command: ${command}`);
}
};
}
private setupWhatsAppEvents(): void {
this.whatsappClient.onQR((qr: string) => {
this.socketServer.sendQR(qr);
});
this.whatsappClient.onStatus((status: string) => {
this.socketServer.sendStatus(status);
logger.info(`WhatsApp status: ${status}`);
});
this.whatsappClient.onMessage(async (message: any) => {
try {
logger.info('Message received via WhatsApp');
if (!message || !message.key) {
logger.warn('Invalid message structure received');
return;
}
// Store in recent messages
this.messagesController.handleMessage(message);
// Forward to webhook
await this.webhookController.receiveMessage({
id: message.key.id || Date.now().toString(),
from: message.key.remoteJid || 'unknown',
content: message.message?.conversation || message.message?.extendedTextMessage?.text || '',
type: message.message?.conversation ? 'text' : 'document',
timestamp: new Date().toISOString()
});
// Notify Manager
this.socketServer.broadcast({
type: 'status',
data: 'message_received'
});
} catch (error) {
logger.error(`Failed to process message: ${error}`);
}
});
}
private async connectWhatsApp(): Promise<void> {
try {
await this.whatsappClient.connect();
logger.info('WhatsApp client initialized');
} catch (error) {
logger.error(`Failed to initialize WhatsApp client: ${error}`);
}
}
private setupRoutes(): void {
// Legacy API Routes (Manager)
this.app.post('/api/send', this.sendController.sendMessage);
this.app.post('/api/send/bulk', this.sendController.sendBulk);
this.app.get('/api/status', this.statusController.getStatus);
this.app.get('/api/health', this.statusController.getHealth);
// Session management (Manager)
this.app.post('/api/session/restart', this.sessionController.restartSession);
this.app.post('/api/session/logout', this.sessionController.logoutSession);
this.app.post('/api/token', this.sessionController.generateToken);
// Messages (Manager)
this.app.get('/api/messages', this.messagesController.getRecentMessages);
this.app.delete('/api/messages', this.messagesController.clearMessages);
// n8n API Core (Component 3)
this.app.post('/api/messages/send', AuthMiddleware.middleware('send'), this.n8nController.sendMessage);
this.app.get('/api/groups', AuthMiddleware.middleware('messages'), this.n8nController.getGroups);
this.app.get('/api/status', AuthMiddleware.middleware('status'), this.n8nController.getStatus);
this.app.post('/api/n8n/token', AuthMiddleware.middleware('status'), this.n8nController.validateToken);
// Public n8n token generation (no auth required)
this.app.post('/api/n8n/generate-token', this.n8nController.generateToken);
// Webhooks for n8n (receiving messages)
this.app.post('/webhook/whatsapp', (req, res) => {
// This endpoint is for n8n to receive messages FROM WhatsApp
res.json({ success: true, message: 'Webhook endpoint active' });
});
// Webhook configuration
this.app.post('/api/webhook/configure', AuthMiddleware.middleware('messages'), this.webhookController.configureWebhook);
this.app.get('/api/webhook/status', AuthMiddleware.middleware('messages'), this.webhookController.getWebhookStatus);
this.app.post('/api/webhook/test', AuthMiddleware.middleware('messages'), this.webhookController.testWebhook);
// Root endpoint
this.app.get('/', (req, res) => {
res.json({
name: 'WhatsApp Gateway',
version: '1.0.0',
status: 'running',
endpoints: {
// Manager endpoints
send: 'POST /api/send',
sendBulk: 'POST /api/send/bulk',
status: 'GET /api/status',
health: 'GET /api/health',
sessionRestart: 'POST /api/session/restart',
sessionLogout: 'POST /api/session/logout',
token: 'POST /api/token',
messages: 'GET /api/messages',
websocket: 'ws://localhost:3003',
// n8n API Core endpoints
n8nSend: 'POST /api/messages/send (Bearer auth required)',
n8nStatus: 'GET /api/status (Bearer auth required)',
n8nToken: 'POST /api/n8n/generate-token',
webhook: 'POST /webhook/whatsapp',
webhookConfig: 'POST /api/webhook/configure',
webhookTest: 'POST /api/webhook/test'
},
authentication: {
type: 'Bearer Token',
header: 'Authorization: Bearer <token>',
generate: 'POST /api/n8n/generate-token'
}
});
});
// Error handling
this.app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.error(`Unhandled error: ${err}`);
res.status(500).json({
success: false,
error: 'Internal server error',
timestamp: new Date().toISOString()
});
});
}
async start(): Promise<void> {
const port = parseInt(process.env.PORT || '3001');
this.app.listen(port, () => {
logger.info(`WhatsApp Gateway API running on port ${port}`);
logger.info(`WebSocket server running on port 3003`);
logger.info(`Manager Web should connect to: http://localhost:3002`);
});
}
async stop(): Promise<void> {
await this.whatsappClient.disconnect();
this.socketServer.close();
logger.info('WhatsApp Gateway stopped');
}
}
// Start the gateway
const gateway = new WhatsAppGateway();
gateway.start().catch(error => {
logger.error(`Failed to start gateway: ${error}`);
process.exit(1);
});
// Graceful shutdown
process.on('SIGINT', async () => {
logger.info('Received SIGINT, shutting down gracefully...');
await gateway.stop();
process.exit(0);
});
process.on('SIGTERM', async () => {
logger.info('Received SIGTERM, shutting down gracefully...');
await gateway.stop();
process.exit(0);
});