const express = require('express'); const admin = require('firebase-admin'); const crypto = require('crypto'); const cluster = require('cluster'); const os = require('os'); const numCPUs = os.cpus().length; if (cluster.isMaster) { console.log(`[Master Process ${process.pid}] Server Active. Detecting ${numCPUs} CPU cores...`); for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker) => { cluster.fork(); }); } else { const app = express(); app.use(express.json()); const workerId = process.pid; // ========================================== // ১. ডাইনামিক CORS (ডোমেন লক) মিডলওয়্যার // ========================================== const allowedDomains = [ 'earnglow.raybil.tech', 'earnglow-es.raybil.tech', 'earnglow-rk.raybil.tech', 'ffyef.raybil.tech' ]; app.use((req, res, next) => { // হেলথ চেক রাউটগুলোর জন্য ডোমেন লক সম্পূর্ণ ওপেন (CORS Bypass) const publicPaths = ['/health', '/ch', '/unps']; if (publicPaths.includes(req.path)) { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); return next(); } // মেইন রিকোয়েস্টগুলোর জন্য কঠোর ডোমেন ভ্যালিডেশন const origin = req.headers.origin; let originDomain = ''; if (origin) { try { originDomain = new URL(origin).hostname; } catch (e) { originDomain = ''; } } if (allowedDomains.includes(originDomain)) { res.setHeader('Access-Control-Allow-Origin', origin); res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // প্রি-ফ্লাইট (OPTIONS) রিকোয়েস্টের জন্য হ্যান্ডেল if (req.method === 'OPTIONS') { return res.sendStatus(200); } next(); } else { // যদি কেউ অননুমোদিত ডোমেন বা পোস্টম্যান দিয়ে ডিরেক্ট হিট করার চেষ্টা করে console.log(`[Worker ${workerId}] Blocked Request from Unauthorized Origin: ${originDomain || 'No Origin'}`); return res.status(403).json({ error: 'CORS Policy: Access Denied for this Origin.' }); } }); // ========================================== // ২. ফায়ারবেজ মাল্টিপল অ্যাডমিন সেটআপ // ========================================== const initFirebase = (envAdmin, envUrl, appName) => { if (!process.env[envAdmin] || !process.env[envUrl]) return null; return admin.initializeApp({ credential: admin.credential.cert(JSON.parse(process.env[envAdmin])), databaseURL: process.env[envUrl] }, appName); }; const app1 = initFirebase('FIREBASE_ADMIN_1', 'FIREBASE_DB_URL_1', 'Admin1'); const app3 = initFirebase('FIREBASE_ADMIN_3', 'FIREBASE_DB_URL_3', 'Admin3'); const app4 = initFirebase('FIREBASE_ADMIN_4', 'FIREBASE_DB_URL_4', 'Admin4'); const db1 = app1 ? app1.database() : null; const db3 = app3 ? app3.database() : null; const db4 = app4 ? app4.database() : null; // ========================================== // ৩. হেলথ চেক রাউটস (যেকোনো ডোমেন থেকে অ্যাক্সেসযোগ্য) // ========================================== const healthHandler = (req, res) => { res.send('Ok, yes, done'); }; app.all('/health', healthHandler); app.all('/ch', healthHandler); app.all('/unps', healthHandler); // ========================================== // ৪. মিডলওয়্যার: শুধুমাত্র POST রিকোয়েস্ট পারমিট করা // ========================================== const checkPostMethod = (req, res, next) => { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method Not Allowed. Only POST is accepted.' }); } next(); }; // ========================================== // ৫. Telegram Security ভ্যালিডেশন মিডলওয়্যার // ========================================== const verifyTelegramAuth = (req, res, next) => { const { init_data, user_id, name } = req.body; if (!init_data) return res.status(401).json({ error: 'Unauthorized: init_data is missing' }); try { const botToken = process.env.BOT_TOKEN; if (!botToken) return res.status(500).json({ error: 'Server configuration error' }); const urlParams = new URLSearchParams(init_data); const hash = urlParams.get('hash'); urlParams.delete('hash'); const dataToCheck = [...urlParams.entries()].map(([key, value]) => `${key}=${value}`).sort().join('\n'); const secretKey = crypto.createHmac('sha256', 'WebAppData').update(botToken).digest(); const calculatedHash = crypto.createHmac('sha256', secretKey).update(dataToCheck).digest('hex'); if (calculatedHash !== hash) return res.status(403).json({ error: 'Forbidden: Invalid Telegram signature' }); const tgUser = JSON.parse(urlParams.get('user')); if (String(tgUser.id) !== String(user_id)) return res.status(403).json({ error: 'Forbidden: User ID mismatch' }); const tgFullName = tgUser.last_name ? `${tgUser.first_name} ${tgUser.last_name}`.trim() : tgUser.first_name; if (String(tgFullName) !== String(name) && String(tgUser.first_name) !== String(name)) { return res.status(403).json({ error: 'Forbidden: Name mismatch' }); } next(); } catch (error) { return res.status(500).json({ error: 'Internal server error during validation' }); } }; // ========================================== // ৬. API: ক্রিয়েট অ্যাকাউন্ট (/creataccoumt) // ========================================== app.post('/creataccoumt', checkPostMethod, verifyTelegramAuth, async (req, res) => { const { user_id, refer_id, name, username, profile_pic_url } = req.body; try { const userRef = db1.ref('user').child(user_id); const snapshot = await userRef.once('value'); if (snapshot.exists()) { const reply = { message: 'Account already exists. No action taken.' }; console.log(`[Worker ${workerId}] User ID: ${user_id} | Status: Existing User | Reply: ${reply.message}`); return res.status(200).json(reply); } const newUserData = { user_id, refer_id: refer_id || null, name: name || null, username: username || null, profile_pic_url: profile_pic_url || null, balance: 0, total_withdraw: 0, total_withdraw_amount: 0, join_date: admin.database.ServerValue.TIMESTAMP, total_refer: 0 }; await userRef.set(newUserData); let logMsg = `[Worker ${workerId}] User ID: ${user_id} | DB1: Updated`; if (refer_id) { const referUserRef = db1.ref('user').child(refer_id); const referUserSnap = await referUserRef.once('value'); if (referUserSnap.exists()) { await referUserRef.child('total_refer').transaction((currentValue) => { return (currentValue || 0) + 1; }); const referListRef = db3.ref('refer').child(refer_id); await referListRef.push(user_id); logMsg += ` & DB3: Refer Updated`; } } const reply = { message: 'Account created successfully!' }; console.log(`${logMsg} | Reply: ${reply.message}`); res.status(201).json(reply); } catch (error) { console.error(`[Worker ${workerId}] User ID: ${user_id || 'Unknown'} | Error in /creataccoumt`); res.status(500).json({ error: 'Internal Server Error' }); } }); // ========================================== // ৭. API: উইথড্র মেইন ব্যালেন্স (/withdraw-main) // ========================================== app.post('/withdraw-main', checkPostMethod, verifyTelegramAuth, async (req, res) => { const { user_id, method, address, time, amount } = req.body; const allowedMethods = ['bkash', 'mobile-gp', 'mobile-ar', 'mobile-ro', 'mobile-bl', 'mobile-tk', 'nagad', 'binance', 'usdt', 'ton']; if (!method || !address || !amount) return res.status(400).json({ error: 'Missing required fields' }); if (!allowedMethods.includes(method)) return res.status(400).json({ error: 'Invalid withdrawal method' }); try { const userRef = db1.ref('user').child(user_id); const snapshot = await userRef.once('value'); if (!snapshot.exists()) return res.status(404).json({ error: 'User not found' }); const userData = snapshot.val(); const currentBalance = Number(userData.balance || 0); const requestAmount = Number(amount); if (currentBalance < requestAmount) return res.status(400).json({ error: 'Insufficient balance' }); await userRef.child('balance').set(currentBalance - requestAmount); const pendingRef = db3.ref('withdrow/painding').child(user_id); await pendingRef.push({ method, address, time: time || admin.database.ServerValue.TIMESTAMP, amount: requestAmount, status: 'pending' }); const reply = { message: 'Withdrawal request successful' }; console.log(`[Worker ${workerId}] User ID: ${user_id} | DB1: Balance Cut | DB3: Request Saved | Reply: ${reply.message}`); res.status(200).json(reply); } catch (error) { console.error(`[Worker ${workerId}] User ID: ${user_id} | Error in /withdraw-main`); res.status(500).json({ error: 'Internal Server Error' }); } }); // ========================================== // ৮. API: অ্যাড ব্যালেন্স উইথড্র (Admin 4 -> Admin 1) // ========================================== const adWithdrawPaths = [ '/withdraw-link-visit', '/withdraw-adsgram-1', '/withdraw-adsgram-2', '/withdraw-gigapub-1', '/withdraw-gigapub-2', '/withdraw-monetag-1', '/withdraw-monetag-2' ]; app.post(adWithdrawPaths, checkPostMethod, async (req, res) => { const { user_id, amount, method } = req.body; if (!user_id || !amount || method !== 'wallet') { return res.status(400).json({ error: 'Invalid request. Method must be wallet and amount is required.' }); } const adNetworkName = req.path.replace('/withdraw-', ''); const requestAmount = Number(amount); try { const adRef = db4.ref(adNetworkName).child(user_id); const adSnapshot = await adRef.once('value'); if (!adSnapshot.exists()) return res.status(404).json({ error: 'No ad earnings found for this user' }); let adCurrentBalance = Number(adSnapshot.val().balance || 0); if (adCurrentBalance < requestAmount) return res.status(400).json({ error: 'Insufficient ad balance' }); await adRef.child('balance').set(adCurrentBalance - requestAmount); const mainUserRef = db1.ref('user').child(user_id); await mainUserRef.child('balance').transaction((currentMainBalance) => { return (currentMainBalance || 0) + requestAmount; }); const reply = { message: `Success! ${requestAmount} transferred to main balance from ${adNetworkName}` }; console.log(`[Worker ${workerId}] User ID: ${user_id} | DB4: Ad Balance Cut | DB1: Main Balance Added | Reply: ${reply.message}`); res.status(200).json(reply); } catch (error) { console.error(`[Worker ${workerId}] User ID: ${user_id} | Error in Ad Withdraw paths`); res.status(500).json({ error: 'Internal Server Error' }); } }); const PORT = process.env.PORT || 7860; app.listen(PORT); }