const express = require('express'); const axios = require('axios'); const app = express(); const serve = require('./vercel-serve'); const path = require('path'); const glob = require('glob'); const matter = require('gray-matter'); const ejs = require('ejs'); const helpers = require('../views/helpers/functions'); // Port number for HTTP server const port = process.env.PORT||3000; // Solr instance URL const solrUrl = 'http://solr.services.cleveland.daball.me:8983/solr/my_core'; // Set EJS as the view engine app.set('view engine', 'ejs'); // Specify the views directory app.set('views', path.join(__dirname, '..', 'views')); // Middleware to parse JSON request body app.use(express.json()); // Middleware to rewrite requests //app.use(rewriter); // // Serve static files (CSS, JavaScript, images, etc.) // app.use(serve('../public', { // dotfiles: 'ignore', // index: false, // })); // app.get('/', (req, res) => { // res.send('Hello World!'); // }) // Endpoints for all the site's pages. glob.globSync('pages/**/*.md', { cwd: path.join(__dirname, '..'), matchBase: true, follow: true, }).forEach((filePath) => { const expressRoutePathFromFilePath = (filePath) => { return filePath.substring('pages'.length, filePath.length - path.extname(filePath).length).replaceAll(path.sep, path.posix.sep); }; const route = expressRoutePathFromFilePath(filePath); const fullFilePath = path.join(__dirname, '..', filePath); let paths = route.split(path.posix.sep); paths[0] = 'public'; app.get(route, async (req, res) => { const fm = matter.read(fullFilePath); const fmData = { fm: fm.data, excerpt: fm.excerpt }; const content = helpers.md.render(fm.content, fmData ); const renderData = { content, route, filePath, fullFilePath, req, paths, ...fmData }; res.render("page", { h: helpers, ...renderData }); }); }); // Search endpoint app.get('/search', async (req, res) => { // Extract search query from request query parameters let { q, page = 1, pageSize = 10 } = req.query; pageSize = Math.min(pageSize, 100); // cap at 100 const query = q; // Calculate start offset for pagination const start = (page - 1) * pageSize; // Sanitize search query to prevent code injection const sanitizedQuery = sanitizeQuery(query); try { // Validate search query if (!query) { //return res.status(400).json({ error: 'q parameter is required' }); res.render('search-error', { h: helpers, query: sanitizedQuery, error: { code: 400, message: 'Search query is required.'} }); } // Send search query to Solr const response = await axios.get(solrUrl + '/select', { params: { q: `text:${sanitizedQuery}`, // Query string with field name hl: 'true', 'hl.method': 'unified', 'hl.fl': '*', 'hl.snippets': 5, 'hl.tag.pre': '', 'hl.tag.post': '', 'hl.usePhraseHighlighter': true, start, // Start offset for pagination rows: 10, // Number of rows to return wt: 'json', // Response format (JSON) }, }); // Extract search results from Solr response const searchResults = response.data.response.docs; const highlightedSnippets = response.data.highlighting; // Calculate total number of results (needed for pagination) const totalResults = response.data.response.numFound; // Calculate total number of pages const totalPages = Math.ceil(totalResults / pageSize); // Send search results as JSON response //res.json('search-results', { query, searchResults, highlightedSnippets, page, pageSize, totalResults, totalPages }); res.render('search-results', { h: helpers, query: sanitizedQuery, searchResults, highlightedSnippets, page, pageSize, totalResults, totalPages }); } catch (error) { // console.error('Error searching Solr:', error.message); // res.status(500).json({ error: 'Internal server error' }); res.render('search-error', { h: helpers, query: sanitizedQuery, error }); } }); // Function to sanitize search query to prevent code injection function sanitizeQuery(query) { // Remove any characters that are not alphanumeric or whitespace return query.replace(/[^\w\s*,."]/gi, ''); } //app.get('/OCR-Encoded-PDFs/Russell-County-Web-Site_2024-02-13_19_50_Modified-With-OCR-Encoding**', rewriter.rewrite('/Web_Site_Archives/Russell_County_Web_Site-2024-02-13_19_50_Modified_With_OCR_Encoding/$1')); app.get('*', async (req, res) => { await serve(req, res, { public: path.join(__dirname, '..', 'public'), symlinks: true, trailingSlash: true, cleanUrls: false, renderSingle: false, unlisted: [ ".DS_Store", ".git", "README*" ], redirects: [ { source: "/:year(\d{4})-:mo(\d{2})-:dd(\d{2})_:hh(\d{2})_:mm(\d{2})/", destination: "/Web_Site_Archives/Russell_County_Web_Site-:year-:mo-:dd_:hh_:mm/" }, { source: "/OCR-Encoded-PDFs", destination: "/Web_Site_Archives" }, { source: "/OCR-Encoded-PDFs/Russell-County-Web-Site_2024-02-13_19_50_Modified-With-OCR-Encoding.zip", destination: "/Web_Site_Archives/Russell_County_Web_Site-2024-02-13_19_50_Modified_With_OCR_Encoding.zip" }, { source: "/OCR-Encoded-PDFs/Russell-County-Web-Site_2024-02-13_19_50_Modified-With-OCR-Encoding/:u(.*)", destination: "/Web_Site_Archives/Russell_County_Web_Site-2024-02-13_19_50_Modified_With_OCR_Encoding:u" }, { source: '/YouTube Channel', destination: '/Russell_County_BOS/YouTube_Channel' }, { source: '/YouTube Channel.zip', destination: '/Russell_County_BOS/YouTube_Channel.zip' }, { source: '/YouTube Channel/:u?', destination: '/Russell_County_BOS/YouTube_Channel/:u' }, { source: '/Project Reclaim [WI19KR9Ogwg].mkv', destination: '/YouTube_Archives/@VADMME/Project Reclaim [WI19KR9Ogwg].mkv' }, ] }); }); // Start server app.listen(port, () => { console.log(`no-moss-3-carbo-landfill-library.online app listening on port ${port}`); });