Raybilhelp commited on
Commit
acd0769
·
verified ·
1 Parent(s): 62261e6

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +79 -134
server.js CHANGED
@@ -1,158 +1,103 @@
1
- require('dotenv').config();
2
  const express = require('express');
3
  const cors = require('cors');
4
  const admin = require('firebase-admin');
5
- const Groq = require('groq-sdk');
6
- const { Octokit } = require('@octokit/rest');
7
 
8
  const app = express();
9
- app.use(cors({ origin: '*' }));
10
- app.use(express.json({ limit: '10mb' }));
11
-
12
- // ১. ফায়ারবেস অ্যাডমিন এসডিকে ইনিশিয়ালিলাইজেশন
13
- try {
14
- const serviceAccount = JSON.parse(process.env.FIREBASE_ADMIN_API);
15
- admin.initializeApp({
16
- credential: admin.credential.cert(serviceAccount),
17
- databaseURL: process.env.FIREBASE_REALTIME_URL
18
- });
19
- console.log("Firebase Connected.");
20
- } catch (error) {
21
- console.error("Firebase Init Error:", error.message);
22
- }
23
- const db = admin.database();
24
-
25
- // ২. Groq এবং GitHub ইনিশিয়ালিলাইজেশন
26
- const groq = new Groq({ apiKey: process.env.GROQ_API });
27
- const octokit = new Octokit({ auth: process.env.GITHUB_API_TOKEN });
28
-
29
- const REPO_OWNER = 'chaingroupworld-rgb';
30
- const REPO_NAME = 'Website-visit-1';
31
-
32
- // 🤖 মাস্টার প্রম্পট: এটি এআই-কে পিওর এসইও এইচটিএমএল ফাইল বানাতে বাধ্য করবে
33
- const MASTER_PROMPT = `You are an expert web developer and storyteller. Generate a unique, interesting short story in Bengali.
34
- The output MUST be a complete, production-ready, standalone HTML page code using Tailwind CSS.
35
- Do NOT include any markdown code blocks like \`\`\`html or explanation. Just start with <!DOCTYPE html> and end with </html>.
36
-
37
- SEO & Design Requirements:
38
- 1. Include <title>, <meta name="description"> (max 160 chars), <meta name="keywords">, and JSON-LD Schema Markup for BlogPosting.
39
- 2. In og:image, use a placeholder or your brand asset logo, but ensure the content has a stunning typographic banner.
40
- 3. For the main body hero section, create a beautiful typography card using Tailwind CSS with dark gradients (slate-900 to indigo-950), glowing neon borders, and large glowing text displaying the story title. This acts as the visual image thumbnail.
41
- 4. The story layout must be clean, readable, responsive for mobile, with elegant fonts. Use appropriate H1, H2 tags.`;
42
-
43
- // 🚀 কোর অটোমেশন ফাংশন (গল্প তৈরি ও গিটহাবে পুশ)
44
- async function generateAndPushStory() {
45
- console.log("Automation triggered: Generating new story...");
46
- try {
47
- // ক) Groq (Llama 4 Scout 17B) থেকে সম্পূর্ণ HTML কোড নেওয়া
48
- const chatCompletion = await groq.chat.completions.create({
49
- messages: [{ role: "user", content: MASTER_PROMPT }],
50
- model: "meta-llama/llama-4-scout-17b-16e-instruct", // বড় কাজের জন্য ১৭বি মডেল
51
- });
52
 
53
- const htmlContent = chatCompletion.choices[0]?.message?.content || "";
54
-
55
- if (!htmlContent.includes("<!DOCTYPE html>")) {
56
- console.error("Invalid HTML output from AI");
57
- return;
58
- }
59
-
60
- // খ) একটি ইউনিক স্লাগ বা ফাইলের নাম তৈরি (টাইমস্ট্যাম্প দিয়ে)
61
- const timestamp = Date.now();
62
- const fileName = `story-${timestamp}.html`;
63
-
64
- // গ) সরাসরি নতুন গল্পের ফাইলটি GitHub-এ পুশ করা
65
- const base64Content = Buffer.from(htmlContent).toString('base64');
66
- await octokit.repos.createOrUpdateFileContents({
67
- owner: REPO_OWNER,
68
- repo: REPO_NAME,
69
- path: fileName,
70
- message: `Automated Story Upload: ${fileName}`,
71
- content: base64Content
72
- });
73
- console.log(`Successfully uploaded: ${fileName}`);
74
 
75
- // ঘ) ফায়ারবেস ট্র্যাক বা মেটাাটা সে করা
76
- const storyRef = db.ref('automated_stories').push();
77
- await storyRef.set({
78
- fileName: fileName,
79
- timestamp: admin.database.ServerValue.TIMESTAMP
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  });
 
 
 
81
 
82
- // 🔄 ঙ) মূল index.html ফাইলটি রিড করে নতুন গল্পের লিংক যুক্ত করা
83
- await updateIndexHtml(fileName);
 
84
 
85
- } catch (error) {
86
- console.error("Automation Logic Error:", error.message);
 
 
 
 
 
87
  }
88
- }
89
 
90
- // 🔄 index.html ডাইনামিকালি আপডেট করার ফাংশน
91
- async function updateIndexHtml(newStoryFile) {
92
  try {
93
- let indexSha, indexContentRaw;
 
 
94
 
95
- // গিটহাব থেকে বর্তমান index.html তুলে আনা
96
- try {
97
- const { data } = await octokit.repos.getContent({
98
- owner: REPO_OWNER,
99
- repo: REPO_NAME,
100
- path: 'index.html',
101
- });
102
- indexSha = data.sha;
103
- indexContentRaw = Buffer.from(data.content, 'base64').toString('utf-8');
104
- } catch (e) {
105
- // যদি index.html না থাকে, তবে একটি বেসিক স্ট্রাকচার তৈরি হবে
106
- indexContentRaw = `<!DOCTYPE html><html><head><title>Raybil Stories</title></head><body class="bg-slate-950 text-white"><h1>গল্পের তালিকা</h1><div id="story-list"></div></body></html>`;
107
- }
108
-
109
- // নতুন গল্পের জন্য লিংকের এইচটিএমএল এলিমেন্ট
110
- const newLinkHtml = `<p><a href="${newStoryFile}" class="text-indigo-400 hover:underline">নতুন গল্প (${new Date().toLocaleTimeString()})</a></p>\n`;
111
-
112
- // কোডের ভেতরের নির্দিষ্ট জায়গাতে (যেমন <div id="story-list"> এর ঠিক নিচে) লিংকটি পুশ করা
113
- let updatedIndexContent;
114
- if (indexContentRaw.includes('<div id="story-list">')) {
115
- updatedIndexContent = indexContentRaw.replace(
116
- '<div id="story-list">',
117
- `<div id="story-list">\n${newLinkHtml}`
118
- );
119
- } else {
120
- // সেফটি ফলব্যাক: যদি ট্যাগ না মেলে তবে বডির শেষে পুশ করবে
121
- updatedIndexContent = indexContentRaw.replace('</body>', `${newLinkHtml}</body>`);
122
- }
123
-
124
- // আপডেট হওয়া index.html গিটহাবে পুশ ব্যাক করা
125
- await octokit.repos.createOrUpdateFileContents({
126
- owner: REPO_OWNER,
127
- repo: REPO_NAME,
128
- path: 'index.html',
129
- message: 'Updated index.html with latest story link',
130
- content: Buffer.from(updatedIndexContent).toString('base64'),
131
- sha: indexSha
132
  });
133
- console.log("index.html successfully updated on GitHub!");
 
 
 
 
 
 
 
 
134
 
135
- } catch (err) {
136
- console.error("Error updating index.html:", err.message);
 
137
  }
138
- }
139
 
140
- // ⏱️ টেস্ট করার ���ন্য প্রতি ২ মিনিট (১২০০০০ মিলিসেকেন্ড) পর পর রান হবে
141
- // যখন ি ঘণ্টা করতে চইবেন, তখন ১২০০০০ বদলে ১৪৪০০০০০ (14400000) দিয়ে দেবেন।
142
- setInterval(generateAndPushStory, 120000);
 
143
 
144
- // বেসিক রুট ও হেলথ চেক
145
- app.get('/', (req, res) => {
146
- res.status(200).send('Raybil Autonomous AI Engine is Running...');
 
 
 
 
 
147
  });
148
 
149
- // ম্যানুয়ালি টেস্ট ট্রিগার করার জন্য একটি গোপন এপিআই রুট
150
- app.get('/api/trigger-now', async (req, res) => {
151
- await generateAndPushStory();
152
- res.json({ success: true, message: "Story generation forced successfully." });
 
153
  });
154
 
155
- const PORT = process.env.PORT || 7860;
156
- app.listen(PORT, () => {
157
- console.log(`Server running on port ${PORT}`);
158
  });
 
 
1
  const express = require('express');
2
  const cors = require('cors');
3
  const admin = require('firebase-admin');
 
 
4
 
5
  const app = express();
6
+ const PORT = process.env.PORT || 7860;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ app.use(cors());
9
+ app.use(express.json());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ // ফায়ারবেস মিন ডিকইনিশিয়ালাইজ করা
12
+ const serviceAccount = require("./firebase-key.json");
13
+ admin.initializeApp({
14
+ credential: admin.credential.cert(serviceAccount)
15
+ });
16
+
17
+ // ==========================================
18
+ // ১. হেলথ চেক রাউট (/health এবং /check)
19
+ // এখানে GET, POST, PUT যাই আসুক না কেন, সার্ভার 'ok' রেসপন্স করবে (ক্রোন জবের জন্য)
20
+ // ==========================================
21
+ const healthCheckHandler = (req, res) => {
22
+ res.status(200).send('ok');
23
+ };
24
+
25
+ app.all('/health', healthCheckHandler);
26
+ app.all('/check', healthCheckHandler);
27
+
28
+ // ==========================================
29
+ // ২. সিকিউরিটি মিডলওয়্যার (Middleware)
30
+ // /subscribe এবং /unsubscribe-এ POST ছাড়া অন্য কোনো রিকোয়েست আসলে তা ব্লক করে দেবে
31
+ // ==========================================
32
+ const enforcePostOnly = (req, res, next) => {
33
+ if (req.method !== 'POST') {
34
+ return res.status(405).json({
35
+ success: false,
36
+ message: `Method ${req.method} Not Allowed. Only POST requests are accepted on this endpoint.`
37
  });
38
+ }
39
+ next();
40
+ };
41
 
42
+ // ==========================================
43
+ // ৩. মূল কার্যকারী রাউট (/subscribe এবং /unsubscribe)
44
+ // ==========================================
45
 
46
+ // --- সাবস্ক্রাইব সেকশন ---
47
+ app.post('/subscribe', enforcePostOnly, async (req, res) => {
48
+ const { fcm_token, selected_category, action } = req.body;
49
+
50
+ // ভ্যালিডেশন এবং অ্যাকশন চেক (অ্যাকশন 'subscribe' হওয়া বাধ্যতামূলক)
51
+ if (!fcm_token || !selected_category || action !== 'subscribe') {
52
+ return res.status(400).json({ success: false, message: "Invalid payload or wrong action for subscription." });
53
  }
 
54
 
 
 
55
  try {
56
+ // ফায়ারবেস টপিক এপিআই কল
57
+ await admin.messaging().subscribeToTopic(fcm_token, selected_category);
58
+ console.log(`Successfully subscribed token to topic: ${selected_category}`);
59
 
60
+ res.status(200).json({
61
+ success: true,
62
+ message: `Successfully registered to ${selected_category} category.`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  });
64
+ } catch (error) {
65
+ console.error("Firebase subscription error:", error);
66
+ res.status(500).json({ success: false, error: error.message });
67
+ }
68
+ });
69
+
70
+ // --- আন-সাবস্ক্রাইব সেকশন ---
71
+ app.post('/unsubscribe', enforcePostOnly, async (req, res) => {
72
+ const { fcm_token, selected_category, action } = req.body;
73
 
74
+ // ভ্যালিডেশন এবং অ্যাকশন চেক (অ্যাকশন 'unsubscribe' হওয়া বাধ্যতামূলক)
75
+ if (!fcm_token || !selected_category || action !== 'unsubscribe') {
76
+ return res.status(400).json({ success: false, message: "Invalid payload or wrong action for unsubscription." });
77
  }
 
78
 
79
+ try {
80
+ // ফায়ারবেস পি থেকে রিমুভ করা এপিআই কল
81
+ await admin.messaging().unsubscribeFromTopic(fcm_token, selected_category);
82
+ console.log(`Successfully unsubscribed token from topic: ${selected_category}`);
83
 
84
+ res.status(200).json({
85
+ success: true,
86
+ message: `Successfully removed from ${selected_category} category.`
87
+ });
88
+ } catch (error) {
89
+ console.error("Firebase unsubscription error:", error);
90
+ res.status(500).json({ success: false, error: error.message });
91
+ }
92
  });
93
 
94
+ // ==========================================
95
+ // ৪. গ্লোবাল ক্যাচ-অল রাউট (অন্য যেকোনো ভুল পাথের জন্য)
96
+ // ==========================================
97
+ app.use((req, res) => {
98
+ res.status(404).json({ success: false, message: "Endpoint not found." });
99
  });
100
 
101
+ app.listen(PORT, '0.0.0.0', () => {
102
+ console.log(`Server is running on port ${PORT}`);
 
103
  });