Spaces:
Paused
Paused
File size: 12,975 Bytes
4a463ef 3c8e8e4 c8eaf5a a192d44 c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a 4a463ef a192d44 4a463ef c8eaf5a a192d44 c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a a192d44 c8eaf5a a192d44 c8eaf5a 4a463ef c8eaf5a 3c8e8e4 c8eaf5a c1099dd c8eaf5a c1099dd 4a463ef c8eaf5a c1099dd 4a463ef c1099dd c8eaf5a 4a463ef c1099dd c8eaf5a c1099dd c8eaf5a 3c8e8e4 c8eaf5a 4a463ef c8eaf5a c1099dd c8eaf5a 4a463ef c8eaf5a 3c8e8e4 c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a 3c8e8e4 4a463ef c8eaf5a 4a463ef c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 c8eaf5a 4a463ef c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 4a463ef 3c8e8e4 c8eaf5a 4a463ef c8eaf5a 3c8e8e4 4a463ef c8eaf5a 4a463ef c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 c8eaf5a 4a463ef c8eaf5a 4a463ef c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 c8eaf5a 4a463ef 3c8e8e4 c8eaf5a 3c8e8e4 c8eaf5a 3c8e8e4 4a463ef c8eaf5a 4a463ef c8eaf5a 3c8e8e4 c8eaf5a 4a463ef | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | 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);
}
|