-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.js
320 lines (253 loc) · 11.6 KB
/
app.js
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
const express = require('express');
const fs = require('fs');
const path = require('path');
const markdown = require('markdown-it')();
const { GoogleGenerativeAI } = require('@google/generative-ai');
const axios = require('axios');
const rateLimit = require('express-rate-limit');
const validator = require('validator');
const cors = require('cors');
require('dotenv').config();
const app = express();
const PORT = 80;
app.use(cors());
// Initialize Google Generative AI
const genAI = new GoogleGenerativeAI(process.env.API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
// Serve static files from the 'public' directory
app.use(express.static('public'));
// Serve any files from the 'nzp' directory
app.use('/nzp', express.static(path.join(__dirname, 'nzp')));
// Route to serve index.html when '/nzp' is accessed
app.get('/nzp', (req, res) => {
res.sendFile(path.join(__dirname, 'nzp', 'index.html'));
});
// Parse URL-encoded bodies (form submissions)
app.use(express.urlencoded({ extended: true }));
// Serve homepage
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'views/index.html'));
});
app.get('/pacman', (req, res) => {
res.sendFile(path.join(__dirname, 'views/pacman.html'));
});
app.get('/slope', (req, res) => {
res.sendFile(path.join(__dirname, 'views/slope.html'));
});
app.get('/about', (req, res) => {
res.sendFile(path.join(__dirname, 'public/about.html'));
});
app.get('/view', (req, res) => {
res.sendFile(path.join(__dirname, 'views/view.html'));
});
// Serve homepage
app.get('/snake', (req, res) => {
res.sendFile(path.join(__dirname, 'views/snake.html'));
});
// In-memory cache for search results
const searchCache = new Map();
// Function to delete all files in the "articles" directory
const deleteArticlesFolder = () => {
const articlesDir = path.join(__dirname, 'public/articles');
fs.readdir(articlesDir, (err, files) => {
if (err) {
console.error(`Error reading the articles directory: ${err}`);
return;
}
files.forEach((file) => {
const filePath = path.join(articlesDir, file);
fs.unlink(filePath, (err) => {
if (err) {
console.error(`Error deleting file ${file}: ${err}`);
} else {
console.log(`Deleted file: ${file}`);
}
});
});
});
};
// Schedule the deleteArticlesFolder function to run every 24 hours
setInterval(deleteArticlesFolder, 24 * 60 * 60 * 1000); // 24 hours in milliseconds
// Function to sanitize scraped data
const sanitizeScrapedData = (text) => {
return text.replace(/[\n\r]/g, ' ').trim(); // Remove newlines, trim whitespace
};
// Function to scrape search results from ScrAPI
const scrapeScrAPI = async (query) => {
if (searchCache.has(query)) {
console.log("Serving from cache");
return searchCache.get(query);
}
const formattedQuery = encodeURIComponent(query);
const url = `https://scrapi.pythonanywhere.com/search?q=${formattedQuery}`;
try {
const { data } = await axios.get(url);
// Check if the response format contains 'results'
if (!data.results || !Array.isArray(data.results)) {
console.error("No results found in the response.");
return [];
}
// Map over the results and extract the 'link' attribute
const links = data.results.map(result => result.link).filter(link => link && link.startsWith('http'));
console.log("Collected URLs:", links);
// Cache the result for 24 hours
searchCache.set(query, links);
setTimeout(() => searchCache.delete(query), 24 * 60 * 60 * 1000);
return links;
} catch (error) {
console.error("Error scraping ScrAPI:", error);
return [];
}
};
// Rate limiter to prevent too many requests
const limiter = rateLimit({
windowMs: 1 * 60 * 1000, // 1 minute window
max: 10, // limit each IP to 10 requests per minute
message: "Too many requests, please try again later.",
});
app.post('/search', limiter, async (req, res) => {
let query = req.body.query;
// Escape user input to prevent XSS attacks
query = validator.escape(query);
// Sanitize the query for use in filenames and URLs
const sanitizedQuery = query.toLowerCase().replace(/[^a-z0-9 ]/g, ' ').trim().replace(/\s+/g, '-');
const filePath = path.join(__dirname, 'public/articles', `${sanitizedQuery}.html`);
// Check if the file already exists
if (fs.existsSync(filePath)) {
return res.redirect(`/articles/${sanitizedQuery}`);
}
try {
// Scrape URLs using ScrAPI
const lookupResult = await scrapeScrAPI(query);
console.log("Scraped URLs:", lookupResult);
if (!Array.isArray(lookupResult) || lookupResult.length === 0) {
const errorMsg = "No results found from ScrAPI. Please try a different query.";
const articleHtml = fs.readFileSync(path.join(__dirname, 'views/template.html'), 'utf8')
.replace(/{{title}}/g, query)
.replace(/{{content}}/g, "No content generated as there were no URLs.")
.replace(/{{urls}}/g, `<li>${errorMsg}</li>`);
fs.writeFileSync(filePath, articleHtml);
return res.redirect(`/articles/${sanitizedQuery}`);
}
// Define mathematical symbols to check for
const mathSymbols = /[+\=\*\^√≥≤π]/;
// Append the scraped links to the prompt
const formattedLinks = lookupResult.map(url => `\n- ${url}`).join('');
const prompt = `You are Infintium. You have two purposes. If the user prompt is a math problem, solve it until it is COMPLETELY simplified. If it is a question, answer it with your own knowledge. If it is an item, such as a toaster, song, or anything that is a statement, act like Wikipedia and provide as much information as possible. USER PROMPT: ${query}. Here are some references you may find helpful (Never metion these links): ${formattedLinks}`;
// Generate content using the AI model
const result = await model.generateContent(prompt);
const markdownContent = markdown.render(result.response.text());
let articleHtml = fs.readFileSync(path.join(__dirname, 'views/template.html'), 'utf8');
articleHtml = articleHtml.replace(/{{title}}/g, query);
articleHtml = articleHtml.replace(/{{content}}/g, markdownContent);
// Check if the query contains math symbols
if (mathSymbols.test(query)) {
// Replace the "Further reading" section with "No further reading - Math"
articleHtml = articleHtml.replace(/{{urls}}/g, '<li>No further reading - Math</li>');
} else {
// Generate the URL list if no math symbols are present
const urlList = lookupResult.map(url => `<li><a href="${url}" target="_blank">${url}</a></li>`).join('');
articleHtml = articleHtml.replace(/{{urls}}/g, urlList);
}
// Write the generated HTML to a file
fs.writeFileSync(filePath, articleHtml);
res.redirect(`/articles/${sanitizedQuery}`);
} catch (error) {
console.error("Error during the search process:", error.message);
res.status(500).send("An unexpected error occurred: " + error.message);
}
});
// Add the new API search route
app.get('/API/search', async (req, res) => {
const query = req.query.q;
const lnks = req.query.lnks === 'true'; // Convert the query parameter to a boolean
const lnknum = parseInt(req.query.lnknum, 10) || 0; // Default to 0 if not provided
// Escape user input to prevent XSS attacks
const escapedQuery = validator.escape(query);
try {
// Scrape URLs using ScrAPI if lnks is true
let lookupResult = [];
if (lnks) {
lookupResult = await scrapeScrAPI(escapedQuery);
// Limit the number of links returned if lnknum is provided
if (lnknum > 0) {
lookupResult = lookupResult.slice(0, lnknum); // Slice to the specified number
}
}
// Define mathematical symbols to check for
const mathSymbols = /[+\=\*\^√≥≤π]/;
// Append the scraped links to the prompt if they exist
const formattedLinks = lookupResult.length > 0 ? lookupResult.map(url => `\n- ${url}`).join('') : '';
const prompt = `You are Infintium. You have two purposes. If the user prompt is a math problem, solve it until it is COMPLETELY simplified. If it is a question, answer it with your own knowledge. If it is an item, such as a toaster, song, or anything that is a statement, act like Wikipedia and provide as much information as possible. USER PROMPT: ${escapedQuery}. Here are some references you may find helpful (Never mention these links): ${formattedLinks}`;
// Generate content using the AI model
const result = await model.generateContent(prompt);
const responseText = result.response.text();
// Create a JSON response
const jsonResponse = {
query: escapedQuery,
answer: responseText,
links: lnks ? lookupResult : [] // Include links only if requested
};
// Send response in plain text JSON format
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(jsonResponse));
} catch (error) {
console.error("Error during the API search process:", error.message);
res.status(500).json({ error: "An unexpected error occurred: " + error.message });
}
});
// Serve suggestions for the autocomplete feature
app.get('/suggest', (req, res) => {
const query = req.query.q.toLowerCase().replace(/-/g, ' ');
const articlesDir = path.join(__dirname, 'public/articles');
fs.readdir(articlesDir, (err, files) => {
if (err) {
return res.status(500).send([]);
}
const suggestions = files
.filter(file => {
const filename = file.replace('.html', '').toLowerCase();
return filename.includes(query);
})
.map(file => file.replace('.html', ''));
res.send(suggestions);
});
});
// Serve the generated article pages or create them if they don't exist
app.get('/articles/:article', async (req, res) => {
const article = req.params.article;
const filePath = path.join(__dirname, 'public/articles', `${article}.html`);
// Check if the file exists
if (fs.existsSync(filePath)) {
return res.sendFile(filePath);
}
try {
const query = article.replace(/-/g, ' ');
const lookupResult = await scrapeScrAPI(query);
if (!Array.isArray(lookupResult) || lookupResult.length === 0) {
const errorMsg = "No content found for this article.";
const articleHtml = fs.readFileSync(path.join(__dirname, 'views/template.html'), 'utf8')
.replace(/{{title}}/g, query)
.replace(/{{content}}/g, "No content generated as there were no URLs.")
.replace(/{{urls}}/g, `<li>${errorMsg}</li>`);
fs.writeFileSync(filePath, articleHtml);
return res.sendFile(filePath);
}
const prompt = `You are Infintium. You have two purposes. If the user prompt is a math problem, solve it until it is COMPLETELY simplified. If it is a question, answer it with your own knowledge. If it is an item, such as a toaster, song, or anything that is a statement, act like Wikipedia and provide as much information as possible. USER PROMPT: ${query}`;
const result = await model.generateContent(prompt);
const markdownContent = markdown.render(result.response.text());
let articleHtml = fs.readFileSync(path.join(__dirname, 'views/template.html'), 'utf8');
articleHtml = articleHtml.replace(/{{title}}/g, query);
articleHtml = articleHtml.replace(/{{content}}/g, markdownContent);
const urlList = lookupResult.map(url => `<li><a href="${url}" target="_blank">${url}</a></li>`).join('');
articleHtml = articleHtml.replace(/{{urls}}/g, urlList);
fs.writeFileSync(filePath, articleHtml);
res.sendFile(filePath);
} catch (error) {
console.error("Error generating the article:", error);
res.status(500).send("An unexpected error occurred: " + error.message);
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});