const express = require('express'); const cors = require('cors'); const admin = require('firebase-admin'); const app = express(); const PORT = process.env.PORT || 7860; app.use(cors()); app.use(express.json()); // ========================================== // ০. ফায়ারবেস এডমিন এসডিকে (Hugging Face Secrets থেকে) // ========================================== try { // Hugging Face Secrets থেকে স্ট্রিংটি নেওয়া হচ্ছে const secretKeyString = process.env.FIREBASE_SERVICE_ACCOUNT; if (!secretKeyString) { throw new Error("FIREBASE_SERVICE_ACCOUNT secret is missing in Hugging Face Spaces settings!"); } // স্ট্রিংটিকে JSON অবজেক্টে রূপান্তর করা const serviceAccount = JSON.parse(secretKeyString); admin.initializeApp({ credential: admin.credential.cert(serviceAccount) }); console.log("Firebase Admin SDK initialized successfully using Hugging Face Secrets!"); } catch (error) { console.error("Firebase Initialization Failed:", error.message); process.exit(1); // সিক্রেট না থাকলে সার্ভার রান করবে না } // ========================================== // ১. হেলথ চেক রাউট (/health এবং /check) // ========================================== const healthCheckHandler = (req, res) => { res.status(200).send('ok'); }; app.all('/health', healthCheckHandler); app.all('/check', healthCheckHandler); // ========================================== // ২. সিকিউরিটি মিডলওয়্যার (Middleware) // ========================================== const enforcePostOnly = (req, res, next) => { if (req.method !== 'POST') { return res.status(405).json({ success: false, message: `Method ${req.method} Not Allowed. Only POST requests are accepted on this endpoint.` }); } next(); }; // ========================================== // ৩. মূল কার্যকারী রাউট (/subscribe এবং /unsubscribe) // ========================================== // --- সাবস্ক্রাইব সেকশন --- app.post('/subscribe', enforcePostOnly, async (req, res) => { const { fcm_token, selected_category, action } = req.body; if (!fcm_token || !selected_category || action !== 'subscribe') { return res.status(400).json({ success: false, message: "Invalid payload or wrong action for subscription." }); } try { await admin.messaging().subscribeToTopic(fcm_token, selected_category); console.log(`Successfully subscribed token to topic: ${selected_category}`); res.status(200).json({ success: true, message: `Successfully registered to ${selected_category} category.` }); } catch (error) { console.error("Firebase subscription error:", error); res.status(500).json({ success: false, error: error.message }); } }); // --- আন-সাবস্ক্রাইব সেকশন --- app.post('/unsubscribe', enforcePostOnly, async (req, res) => { const { fcm_token, selected_category, action } = req.body; if (!fcm_token || !selected_category || action !== 'unsubscribe') { return res.status(400).json({ success: false, message: "Invalid payload or wrong action for unsubscription." }); } try { await admin.messaging().unsubscribeFromTopic(fcm_token, selected_category); console.log(`Successfully unsubscribed token from topic: ${selected_category}`); res.status(200).json({ success: true, message: `Successfully removed from ${selected_category} category.` }); } catch (error) { console.error("Firebase unsubscription error:", error); res.status(500).json({ success: false, error: error.message }); } }); // ========================================== // ৪. গ্লোবাল ক্যাচ-অল রাউট // ========================================== app.use((req, res) => { res.status(404).json({ success: false, message: "Endpoint not found." }); }); app.listen(PORT, '0.0.0.0', () => { console.log(`Server is running on port ${PORT}`); });