diff --git a/app.js b/app.js index ed3232e..f3b9eb8 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,7 @@ 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'); @@ -8,7 +9,7 @@ const validator = require('validator'); require('dotenv').config(); const app = express(); -const PORT = 80; // Change the port if needed +const PORT = 80; // Initialize Google Generative AI const genAI = new GoogleGenerativeAI(process.env.API_KEY); @@ -19,25 +20,49 @@ app.use(express.static('public')); // Parse URL-encoded bodies (form submissions) app.use(express.urlencoded({ extended: true })); -app.use(express.json()); // To parse JSON in POST requests - -// 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.", -}); // Serve homepage app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'views/index.html')); }); -// Serve the Algebra Calculator page -app.get('/algebra', (req, res) => { - res.sendFile(path.join(__dirname, 'views/algebra.html')); +app.get('/pacman', (req, res) => { + res.sendFile(path.join(__dirname, 'views/pacman.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 @@ -45,175 +70,226 @@ const sanitizeScrapedData = (text) => { // Function to scrape search results from SerpAPI const scrapeSerpApiSearch = async (query) => { - const apiKey = process.env.SERPAPI_API_KEY; // Add your SerpAPI key in the .env file + if (searchCache.has(query)) { + console.log("Serving from cache"); + return searchCache.get(query); + } + + const apiKey = process.env.SERPAPI_API_KEY; const formattedQuery = encodeURIComponent(query); const url = `https://serpapi.com/search.json?q=${formattedQuery}&api_key=${apiKey}`; try { const { data } = await axios.get(url); - // Check if the response contains organic_results if (!data.organic_results || !Array.isArray(data.organic_results)) { console.error("No organic results found in the response."); return []; } const links = data.organic_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; // Return an array of links + return links; } catch (error) { console.error("Error scraping SerpAPI:", error); - return []; // Return an empty array in case of error + return []; } }; -// Handle Algebra calculation requests (AJAX) -app.post('/algebra-calculate', async (req, res) => { - let query = req.body.query; +// Function to scrape images from SerpAPI +const scrapeSerpApiImages = async (query) => { + if (searchCache.has(query)) { + console.log("Serving images from cache"); + return searchCache.get(query); + } - // Sanitize user input - query = validator.escape(query); + const apiKey = process.env.SERPAPI_API_KEY; + const url = `https://serpapi.com/search.json?engine=google_images&q=${query}&api_key=${apiKey}`; try { - // Modify the prompt for Algebra problem solving - const prompt = `Solve the following algebra problem step-by-step and simplify completely: ${query}`; + const { data } = await axios.get(url); + const images = data.images_results.slice(0, 10).map(img => ({ + thumbnail: img.thumbnail, + original: img.original + })); - // Generate AI content using Google Generative AI model - const result = await model.generateContent(prompt); + // Cache the result for 24 hours + searchCache.set(query, images); + setTimeout(() => searchCache.delete(query), 24 * 60 * 60 * 1000); - // Send the AI-generated content directly back to the client - res.send(`
${result.response.text()}
`); + return images; } catch (error) { - console.error("Error during algebra processing:", error.message); - res.status(500).send("An unexpected error occurred while solving the algebra problem."); + console.error("Error scraping SerpAPI images:", error); + return []; } -}); - -// 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 +// 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.", +}); // Handle search form submissions app.post('/search', limiter, async (req, res) => { let query = req.body.query; - // Sanitize user input using validator query = validator.escape(query); 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 article already exists if (fs.existsSync(filePath)) { return res.redirect(`/articles/${sanitizedQuery}`); } try { - // Scrape information from SerpAPI const lookupResult = await scrapeSerpApiSearch(query); + console.log("Scraped URLs:", lookupResult); - // Ensure lookupResult is an array if (!Array.isArray(lookupResult) || lookupResult.length === 0) { - // Provide an error response and include a message const errorMsg = "No results found from SerpAPI. 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, `No images available
'; + + articleHtml = articleHtml.replace(/{{imageGallery}}/g, imageGallery); + + // Save the generated HTML file + fs.writeFileSync(filePath, articleHtml); + res.sendFile(filePath); + } catch (imageError) { + console.error("Error generating the image gallery:", imageError); + res.status(500).send("Error generating the image gallery."); + } + } 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 http://localhost:${PORT}`); + console.log(`Server is running on port ${PORT}`); }); diff --git a/public/css/styles.css b/public/css/styles.css index 814205d..c21979a 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -1,127 +1,88 @@ -/* General styles */ +/* Existing styles */ body { font-family: Arial, sans-serif; text-align: center; background-color: #f8f9fa; - margin: 0; - padding: 0; } .container { - margin-top: 50px; + margin-top: 100px; display: flex; flex-direction: column; align-items: center; - padding: 20px; } -/* Styling for the math logo */ -.math-logo { - width: 200px; - height: auto; - margin-bottom: 30px; -} - -/* Input area styles */ -textarea { - width: 80%; - max-width: 600px; - padding: 10px; - font-size: 16px; - border: 1px solid #ccc; - border-radius: 5px; +.logo { + width: 350px; margin-bottom: 20px; } -/* Calculator pad styles */ -.calculator-pad { - display: flex; - flex-wrap: wrap; - justify-content: center; - margin-bottom: 20px; -} - -.calculator-pad button { - width: 50px; - height: 50px; - margin: 5px; - font-size: 18px; - background-color: #007bff; - color: white; - border: none; - border-radius: 5px; - cursor: pointer; +.search-container { display: flex; - justify-content: center; align-items: center; + position: relative; } -.calculator-pad button:hover { - background-color: #0056b3; +input[type="text"] { + width: 60%; + max-width: 600px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; + margin-right: 10px; } -/* Submit button styles */ -button[type="submit"] { +button { padding: 10px 20px; font-size: 16px; - background-color: #28a745; + background-color: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; - margin-bottom: 20px; -} - -button[type="submit"]:hover { - background-color: #218838; } -/* Loading island */ -.loading-island { - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 20px; +button:hover { + background-color: #0056b3; } -.loading-spinner { - border: 4px solid rgba(0, 0, 0, 0.1); - border-left-color: #007bff; - border-radius: 50%; - width: 30px; - height: 30px; - animation: spin 1s linear infinite; +.suggestions-list { + list-style-type: none; + padding: 0; + margin: 5px 0 0 0; + border: 1px solid #ccc; + max-width: 400px; + background-color: white; + position: relative; + z-index: 1000; + width: 60%; + display: none; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } -@keyframes spin { - to { transform: rotate(360deg); } +.suggestion-item { + padding: 10px; + cursor: pointer; } -/* Result container */ -.result-container { +.suggestion-item:hover { background-color: #f1f1f1; - border-radius: 5px; - padding: 20px; - max-width: 600px; - width: 80%; - margin-bottom: 20px; - font-size: 18px; } -/* Footer text */ -p { - font-size: 14px; - color: #666; - margin-bottom: 20px; +/* Show suggestions when there are items */ +#searchInput:focus + .suggestions-list, +.suggestions-list:has(.suggestion-item) { + display: block; } -/* Dropdown styles */ +/* New styles for the Math Symbols dropdown */ #mathSymbolsDropdown { - padding: 10px; + padding: 5px; font-size: 15px; border: 1px solid #ccc; border-radius: 5px; - margin: 10px 0; + margin-left: 5px; background-color: white; cursor: pointer; } @@ -136,23 +97,69 @@ p { font-size: 15px; } -/* Responsive design */ -@media (max-width: 768px) { - textarea { - width: 90%; - } +/* New styles for image gallery */ +.image-gallery { + display: flex; + justify-content: center; + flex-wrap: wrap; + margin: 20px 0; +} - .calculator-pad button { - width: 40px; - height: 40px; - font-size: 16px; - } +.image-gallery img { + width: 200px; + height: 150px; + margin: 10px; + border-radius: 8px; + transition: transform 0.3s ease, box-shadow 0.3s ease; + cursor: pointer; +} + +.image-gallery img:hover { + transform: scale(1.05); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.image-gallery img:active { + transform: scale(1.1); +} + +/* Enlarged image modal */ +#imageModal { + display: none; + position: fixed; + z-index: 1001; + padding-top: 60px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.8); +} - button[type="submit"] { - width: 80%; - } +#imageModal img { + display: block; + margin: auto; + max-width: 90%; + max-height: 80%; + border-radius: 8px; +} + +#imageModal .close { + position: absolute; + top: 15px; + right: 25px; + color: white; + font-size: 35px; + font-weight: bold; + cursor: pointer; +} + +#imageModal .close:hover { + color: #ccc; +} - .result-container { - width: 90%; - } +/* Image download on double-click */ +.image-gallery img:active { + cursor: pointer; } diff --git a/public/mathLogo.png b/public/mathLogo.png deleted file mode 100644 index 7084d43..0000000 Binary files a/public/mathLogo.png and /dev/null differ diff --git a/views/algebra.html b/views/algebra.html deleted file mode 100644 index 87c826c..0000000 --- a/views/algebra.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - -Loading, please wait...
-Infinite Algebra Calculations with Infintium.
-Infinite Searches, Infinite Learning.
-Ver. 0.91.7
+Ver. 0.99.8
GitHub About -P.S. Also try Infintium Algebra for quick solving!
+P.S. Also try entering algebra problems for quick solving!