Spaces:
Paused
Paused
Create app.js
Browse files
app.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const express = require('express');
|
| 2 |
+
const admin = require('firebase-admin');
|
| 3 |
+
|
| 4 |
+
const app = express();
|
| 5 |
+
app.use(express.json());
|
| 6 |
+
|
| 7 |
+
// ==========================================
|
| 8 |
+
// ১. ফায়ারবেজ সেটআপ (Environment Variable থেকে)
|
| 9 |
+
// ==========================================
|
| 10 |
+
// Hugging Face-এর Secrets এ FIREBASE_ADMIN JSON স্ট্রিং হিসেবে সেভ করা থাকতে হবে
|
| 11 |
+
// এবং FIREBASE_DB_URL নামে ডাটাবেজ লিংকটি রাখতে হবে।
|
| 12 |
+
const serviceAccount = JSON.parse(process.env.FIREBASE_ADMIN);
|
| 13 |
+
|
| 14 |
+
admin.initializeApp({
|
| 15 |
+
credential: admin.credential.cert(serviceAccount),
|
| 16 |
+
databaseURL: process.env.FIREBASE_DB_URL // উদাহরণ: "https://your-project.firebaseio.com"
|
| 17 |
+
});
|
| 18 |
+
const db = admin.database();
|
| 19 |
+
|
| 20 |
+
// ==========================================
|
| 21 |
+
// ২. হেলথ চেক রাউটস (যেকোনো রিকোয়েস্ট)
|
| 22 |
+
// ==========================================
|
| 23 |
+
const healthHandler = (req, res) => {
|
| 24 |
+
res.send('Ok yes done');
|
| 25 |
+
};
|
| 26 |
+
app.all('/health', healthHandler);
|
| 27 |
+
app.all('/check', healthHandler);
|
| 28 |
+
app.all('/unps', healthHandler);
|
| 29 |
+
|
| 30 |
+
// ==========================================
|
| 31 |
+
// ৩. মূল কাজের পাথগুলো
|
| 32 |
+
// ==========================================
|
| 33 |
+
const pathGroup1 = ['/link-visit'];
|
| 34 |
+
const pathGroup2 = ['/adsgram-1', '/adsgram-2', '/gigapub-1', '/gigapub-2', '/monetag-1', '/monetag-2', '/add-account'];
|
| 35 |
+
const allActionPaths = [...pathGroup1, ...pathGroup2];
|
| 36 |
+
|
| 37 |
+
// শুধুমাত্র POST রিকোয়েস্ট পারমিট করার মিডলওয়্যার
|
| 38 |
+
app.use(allActionPaths, (req, res, next) => {
|
| 39 |
+
if (req.method !== 'POST') {
|
| 40 |
+
return res.status(405).json({ error: 'Method Not Allowed. Only POST is accepted.' });
|
| 41 |
+
}
|
| 42 |
+
next();
|
| 43 |
+
});
|
| 44 |
+
|
| 45 |
+
// ==========================================
|
| 46 |
+
// ৪. Cloudflare Turnstile ভ্যালিডেশন মিডলওয়্যার
|
| 47 |
+
// ==========================================
|
| 48 |
+
const verifyCloudflare = async (req, res, next) => {
|
| 49 |
+
// ক্লায়েন্ট থেকে আসা টোকেন (ধরে নিচ্ছি বডিতে 'cf_token' নামে আসবে)
|
| 50 |
+
const token = req.body.cf_token;
|
| 51 |
+
if (!token) return res.status(403).json({ error: 'Cloudflare token is missing' });
|
| 52 |
+
|
| 53 |
+
try {
|
| 54 |
+
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
|
| 55 |
+
method: 'POST',
|
| 56 |
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
| 57 |
+
body: `secret=${process.env.CLOUDFLIRE_CHECK}&response=${token}`
|
| 58 |
+
});
|
| 59 |
+
|
| 60 |
+
const data = await response.json();
|
| 61 |
+
if (data.success) {
|
| 62 |
+
next(); // ভ্যালিডেশন সফল, পরের ধাপে যাও
|
| 63 |
+
} else {
|
| 64 |
+
return res.status(403).json({ error: 'Cloudflare verification failed' });
|
| 65 |
+
}
|
| 66 |
+
} catch (error) {
|
| 67 |
+
return res.status(500).json({ error: 'Server error during Cloudflare verification' });
|
| 68 |
+
}
|
| 69 |
+
};
|
| 70 |
+
|
| 71 |
+
// ==========================================
|
| 72 |
+
// ৫. ডাটা প্রসেসিং এবং ফায়ারবেজে সেভ করা
|
| 73 |
+
// ==========================================
|
| 74 |
+
app.post(allActionPaths, verifyCloudflare, async (req, res) => {
|
| 75 |
+
const { user_id, action, 'action_type-1': balance, 'action_type-2': stock } = req.body;
|
| 76 |
+
|
| 77 |
+
// ভ্যালিডেশন: ইউজার আইডি এবং অ্যাকশন চেক
|
| 78 |
+
if (!user_id || !action) {
|
| 79 |
+
return res.status(400).json({ error: 'Missing user_id or action' });
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
// ভ্যালিডেশন: ডাটা ডিলিট করার রিকোয়েস্ট বাতিল
|
| 83 |
+
if (action.toLowerCase().includes('delete') || action.toLowerCase() !== 'add') {
|
| 84 |
+
return res.status(400).json({ error: 'Invalid action. Only "add" is allowed.' });
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
const currentPath = req.path; // যেমন: '/link-visit'
|
| 88 |
+
|
| 89 |
+
// ভ্যালিডেশন: ফিক্সড ব্যালেন্স এবং স্টক চেক
|
| 90 |
+
if (pathGroup1.includes(currentPath)) {
|
| 91 |
+
if (Number(balance) !== 0.05 || stock !== '+1') {
|
| 92 |
+
return res.status(400).json({ error: 'Invalid balance or stock for this path. Expected 0.05 and +1.' });
|
| 93 |
+
}
|
| 94 |
+
} else if (pathGroup2.includes(currentPath)) {
|
| 95 |
+
if (Number(balance) !== 0.06 || stock !== '+1') {
|
| 96 |
+
return res.status(400).json({ error: 'Invalid balance or stock for this path. Expected 0.06 and +1.' });
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
// ফায়ারবেজে ডাটা সেভ করা
|
| 101 |
+
try {
|
| 102 |
+
// ফায়ারবেজে রিকোয়েস্টের পাথের নামেই ডাটা সেভ হবে (যেমন: /link-visit/user_id/...)
|
| 103 |
+
const dbRef = db.ref(currentPath).child(user_id);
|
| 104 |
+
|
| 105 |
+
const newData = {
|
| 106 |
+
action: action,
|
| 107 |
+
balance: Number(balance),
|
| 108 |
+
stock: stock,
|
| 109 |
+
timestamp: admin.database.ServerValue.TIMESTAMP
|
| 110 |
+
};
|
| 111 |
+
|
| 112 |
+
// ইউজারের নোডের আন্ডারে নতুন ডাটা পুশ করা হলো
|
| 113 |
+
await dbRef.push(newData);
|
| 114 |
+
|
| 115 |
+
res.status(200).json({ message: 'Success! Data saved to Firebase.', path: currentPath });
|
| 116 |
+
} catch (error) {
|
| 117 |
+
console.error('Firebase Error:', error);
|
| 118 |
+
res.status(500).json({ error: 'Failed to save data to Firebase' });
|
| 119 |
+
}
|
| 120 |
+
});
|
| 121 |
+
|
| 122 |
+
// সার্ভার স্টার্ট
|
| 123 |
+
const PORT = process.env.PORT || 7860; // Hugging Face Space ডিফল্টভাবে 7860 পোর্ট ব্যবহার করে
|
| 124 |
+
app.listen(PORT, () => {
|
| 125 |
+
console.lang(`Server is running on port ${PORT}`);
|
| 126 |
+
});
|