|
| 1 | +import http from 'http'; |
| 2 | +import path from 'path'; |
| 3 | +import cors from 'cors'; |
| 4 | +import express, { Application } from 'express'; |
| 5 | +import { Server as SocketServer } from 'socket.io'; |
| 6 | + |
| 7 | +import { errorHandler } from '../middleware/errors'; |
| 8 | +import v1Router from '../routes'; |
| 9 | +import { WhatsappBotInstance } from '../utils/integrations/whatsapp'; |
| 10 | +import appConfig from './app'; |
| 11 | +import { limiter } from './limiter'; |
| 12 | + |
| 13 | +// Map untuk menyimpan instance bot |
| 14 | +const botInstances: Map<string, WhatsappBotInstance> = new Map(); |
| 15 | +const MAX_INSTANCES = 3; // Batas untuk 1GB RAM |
| 16 | + |
| 17 | +export class Server { |
| 18 | + public app: Application; |
| 19 | + public port: number; |
| 20 | + private io: SocketServer; |
| 21 | + private server: http.Server; |
| 22 | + |
| 23 | + constructor(port: number) { |
| 24 | + this.app = express(); |
| 25 | + this.port = port; |
| 26 | + this.server = http.createServer(this.app); |
| 27 | + this.io = new SocketServer(this.server, { |
| 28 | + cors: { |
| 29 | + origin: appConfig.CLIENT_ORIGIN, |
| 30 | + }, |
| 31 | + }); |
| 32 | + this.initializeMiddlewares(); |
| 33 | + this.initializeRoutes(); |
| 34 | + this.initializeSocket(); |
| 35 | + } |
| 36 | + |
| 37 | + private initializeMiddlewares() { |
| 38 | + // Add your middlewares here |
| 39 | + this.app.use(express.json()); |
| 40 | + this.app.use(cors()); |
| 41 | + this.app.use(limiter); |
| 42 | + } |
| 43 | + |
| 44 | + private initializeRoutes() { |
| 45 | + // Add your routes here |
| 46 | + |
| 47 | + // Serve the static files from the React app |
| 48 | + this.app.get('/', (_req, res) => { |
| 49 | + res.sendFile(path.join(__dirname, '../../web/dist/index.html')); |
| 50 | + }); |
| 51 | + this.app.use( |
| 52 | + express.static(path.join(__dirname, '../../web/dist'), { |
| 53 | + maxAge: '1d', |
| 54 | + }) |
| 55 | + ); |
| 56 | + |
| 57 | + // Endpoint untuk memulai bot |
| 58 | + this.app.get('/api/start-bot', async (req, res) => { |
| 59 | + const { userId, agentId } = req.query; |
| 60 | + if (!userId || !agentId) { |
| 61 | + return res.status(400).json({ error: 'Missing userId or agentId' }); |
| 62 | + } |
| 63 | + |
| 64 | + if (botInstances.has(userId as string)) { |
| 65 | + return res.json({ message: `Bot for ${userId} already running` }); |
| 66 | + } |
| 67 | + |
| 68 | + if (botInstances.size >= MAX_INSTANCES) { |
| 69 | + const oldestUserId = botInstances.keys().next().value; |
| 70 | + const oldestBot = botInstances.get(oldestUserId!); |
| 71 | + await oldestBot?.disconnect(); |
| 72 | + botInstances.delete(oldestUserId!); // Hapus dari Map setelah disconnect |
| 73 | + } |
| 74 | + |
| 75 | + const bot = new WhatsappBotInstance( |
| 76 | + { userId: userId as string, agentId: agentId as string }, |
| 77 | + this.io |
| 78 | + ); |
| 79 | + botInstances.set(userId as string, bot); |
| 80 | + res.json({ message: `Bot for ${userId} started. Please check QR code.` }); |
| 81 | + }); |
| 82 | + |
| 83 | + // V1 Routes |
| 84 | + this.app.use('/api', v1Router); |
| 85 | + |
| 86 | + // Error Handler |
| 87 | + this.app.use(errorHandler); |
| 88 | + |
| 89 | + // Catch-all route to handle client-side routing |
| 90 | + this.app.get('*', (_req, res) => { |
| 91 | + res.sendFile(path.join(__dirname, '../../web/dist/index.html')); |
| 92 | + }); |
| 93 | + } |
| 94 | + |
| 95 | + private initializeSocket() { |
| 96 | + this.io.on('connection', (socket) => { |
| 97 | + console.log('Client connected: ', socket.id); |
| 98 | + |
| 99 | + socket.on('join', (userId) => { |
| 100 | + socket.join(userId); |
| 101 | + console.log(`Client ${socket.id} joined room ${userId}`); |
| 102 | + }); |
| 103 | + |
| 104 | + socket.on('disconnect', () => { |
| 105 | + console.log('Client disconnected: ', socket.id); |
| 106 | + }); |
| 107 | + }); |
| 108 | + } |
| 109 | + |
| 110 | + public listen() { |
| 111 | + this.server.listen(this.port, () => { |
| 112 | + console.log(`Server is running on ${this.getBaseUrl()}`); |
| 113 | + }); |
| 114 | + } |
| 115 | + |
| 116 | + private getBaseUrl() { |
| 117 | + return `http://localhost:${this.port}`; |
| 118 | + } |
| 119 | + |
| 120 | + public getIo() { |
| 121 | + return this.io; |
| 122 | + } |
| 123 | + |
| 124 | + public async shutdown() { |
| 125 | + // for (const ) |
| 126 | + } |
| 127 | +} |
0 commit comments