UNPKG

karshard

Version:

Karshard: Fastify + System Information API with Load Balancer support for GCP, Azure, and Cloudflare. More info: https://github.com/DeveloperKubilay/Karshard

211 lines (184 loc) 8.28 kB
const fs = require('fs'); const logger = require('../log.js') function plugin(wss, options) { const logg = new logger(options.config.log.file); logg.newSession(); var servers = {}; const recentlyRedirected = new Map(); let VmCreatedAt = null; let requestCounts = []; let attackModeActive = false; let attackModeActivatedAt = null; function broadcastToDashboard(type, data) { wss.clients.forEach(client => { if (!client[type]) return; if (client.readyState === 1) { try { client.send(JSON.stringify({ [type]: true, loadbalancer: true, ...data })); } catch (e) { } } }); } setInterval(() => { const now = Date.now(); // yönlendirilmiş sunucuları temizle for (const [ip, timestamp] of recentlyRedirected.entries()) { if (now - timestamp > options.config.CpuReleaseUsage.RoutingDelay) { recentlyRedirected.delete(ip); } } const serverList = Object.entries(servers).map(([ip, server]) => { if (server.ws.dashboard) return null; if (server.ws.host) return null; const lastStatus = server.status[server.status.length - 1]; return { ip, ws: server.ws, cpu: lastStatus ? Number(lastStatus.cpu) : null, mem: lastStatus ? Number(lastStatus.mem) : null, reqCount: lastStatus ? Number(lastStatus.reqCount) : 0, reqBytes: lastStatus ? Number(lastStatus.reqBytes) : 0, resBytes: lastStatus ? Number(lastStatus.resBytes) : 0 }; }).filter(z => z) const idleServers = serverList //boşta olan sunucular .filter(s => s.cpu !== null && s.cpu < options.config.CpuReleaseUsage.max) .sort((a, b) => a.cpu - b.cpu); const filteredServers = serverList.filter(s => !recentlyRedirected.has(s.ip));//Max geçmiş sunucular for (let i = 0; i < filteredServers.length; i++) { const server = filteredServers[i];//Max geçmiş sunucu if (server.cpu !== null && server.cpu > options.config.CpuReleaseUsage.prepare_threshold) { const target = idleServers.find(s => s.ip !== server.ip && !recentlyRedirected.has(s.ip)); if (target) { recentlyRedirected.set(target.ip, now); recentlyRedirected.set(server.ip, now); server.ws.send(JSON.stringify({ loadbalancer: true, redirect: options.config.allowedIpaddrs.find(z => z.ip === target.ip) })); } } } const nowIdleServers = (idleServers.find(s => !recentlyRedirected.has(s.ip)) || idleServers[0])?.ip; if (nowIdleServers) { broadcastToDashboard("host", { ip: nowIdleServers, url: options.config.allowedIpaddrs.find(z => z.ip === nowIdleServers)?.url }); } for (const server of serverList) { if (server.cpu >= options.config.CpuReleaseUsage.max) { if (!VmCreatedAt || VmCreatedAt < now - options.config.CpuReleaseUsage.CreateVmTimeout) { options?.createVm(servers, options.config.allowedIpaddrs); VmCreatedAt = now; break; } } } // Ortalama CPU, RAM, istek sayısı, alınan ve gönderilen KB bilgilerini hesapla const avgCpu = serverList.length > 0 ? (serverList.reduce((sum, s) => sum + (s.cpu || 0), 0) / serverList.length).toFixed(2) : 0; const avgMem = serverList.length > 0 ? (serverList.reduce((sum, s) => sum + (s.mem || 0), 0) / serverList.length).toFixed(2) : 0; const totalReqCount = serverList.reduce((sum, s) => sum + s.reqCount, 0); const totalReqBytes = serverList.reduce((sum, s) => sum + s.reqBytes, 0); const totalResBytes = serverList.reduce((sum, s) => sum + s.resBytes, 0); // İstek sayısı analizi requestCounts.push(totalReqCount); if (requestCounts.length > 200) { requestCounts.shift(); } if (requestCounts.length === 200) { const avgRequests = requestCounts.reduce((sum, count) => sum + count, 0) / requestCounts.length; if (!attackModeActive && avgRequests > options.config.UnderAttack.minRequests && totalReqCount > avgRequests * options.config.UnderAttack.thresholdIncrease) { options.cloudflare.UnderAttackMode(true); attackModeActive = true; logg.log(options.config.log.attackInfo, 'Under Attack Mode activated'); attackModeActivatedAt = now; } } // Attack mode kapatma kontrolü if (attackModeActive && attackModeActivatedAt && (now - attackModeActivatedAt > options.config.UnderAttack.minDuration)) { // Süre geçti mi? const avgRequests = requestCounts.reduce((sum, count) => sum + count, 0) / requestCounts.length; if (totalReqCount <= avgRequests * options.config.UnderAttack.thresholdDecrease) { options.cloudflare.UnderAttackMode(false); attackModeActive = false; console.log('Under Attack Mode deactivated'); logg.log(options.config.log.attackInfo, 'Under Attack Mode deactivated'); attackModeActivatedAt = null; } } broadcastToDashboard("dashboard", { serverCount: serverList.length, avgCpu: avgCpu, avgMem: avgMem, totalRequests: totalReqCount, dataReceived: (totalReqBytes / 1024).toFixed(2), dataSent: (totalResBytes / 1024).toFixed(2), servers: serverList.map(s => ({ ip: s.ip, cpu: s.cpu, mem: s.mem, reqCount: s.reqCount, reqBytes: s.reqBytes, resBytes: s.resBytes })) }); }, options.Interval || 1000); wss.on('connection', async (ws, req) => { if(options.config.log.debug) console.log('WebSocket bağlantısı kuruldu.'); if (!req.url) { ws.close(4000, 'No URL'); return; } const urlParams = new URLSearchParams(req.url.slice(req.url.indexOf('?') + 1)); const token = urlParams.get('token'); const dashboard = urlParams.get('dashboard'); const host = urlParams.get('host'); if (dashboard) ws.dashboard = true; if (host) ws.host = true; if (token !== options.token) { ws.close(4000, 'Invalid Token'); return; } var ipaddr = req.socket.remoteAddress || req.socket.localAddress; if (!options.config.allowedIpaddrs.find(z => z.ip === ipaddr)) { ws.close(4000, 'IP Address Not Allowed'); return; } if (process.env.KARSHARD_DEVMODE) { ipaddr = Array(4).fill(0).map(() => Math.floor(Math.random() * 256)).join('.'); options.config.allowedIpaddrs.push({ ip: ipaddr, url: `http://${ipaddr}` }); } ws.ipaddr = ipaddr; if (servers[ipaddr]) { servers[ipaddr].ws?.close() delete servers[ipaddr] } servers[ipaddr] = { ws: ws, status: [] }; ws.on('message', (data) => { try { const obj = JSON.parse(data); if (!obj.loadbalancer) return; delete obj.loadbalancer; if (servers[ipaddr].status.length > 200) { servers[ipaddr].status.shift(); } servers[ipaddr].status.push(obj); } catch (e) {} }); ws.on('error', (error) => { }); ws.on('close', (code, reason) => { delete servers[ipaddr]; }); }); } module.exports = plugin;