Spaces:
Paused
Paused
| 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); | |
| } | |