Earnglow-4 / app.js
Raybilhelp's picture
Update app.js
4a463ef verified
Raw
History Blame Contribute Delete
13 kB
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);
}