Updated environment configuration and cleaned up usage of runtime environment.

This commit is contained in:
David Ball 2024-06-15 15:03:04 -04:00
parent 8a28910b0b
commit 5f4ac46af7
6 changed files with 129 additions and 68 deletions

View File

@ -1,20 +1,27 @@
# You will need a .env file to use with the Docker containers. This is set up for localhost use with the Docker Compose fullstack. # You will need a .env file to use with the Docker containers. This is set up for localhost use with the Docker Compose fullstack.
# APP_HTTP_LISTEN_PORT is the TCP port used to access the Node application's HTTP interface (usually by a reverse proxy).
APP_HTTP_HOST="nm3clol-express-app"
# APP_HTTP_LISTEN_PORT is the TCP port used to access the Node application's HTTP interface (usually by a reverse proxy).
APP_HTTP_PORT=3000
# APP_URL is the URL used to access the Node application (usually by a reverse proxy).
APP_HTTP_URL="http://${APP_HTTP_HOST}:${APP_HTTP_PORT}"
# SITE_NAME is used for page generation. # SITE_NAME is used for page generation.
SITE_NAME="(dev) No Moss 3 Carbo Landfill Online Localhost" SITE_NAME="(dev) No Moss 3 Carbo Landfill Online Localhost"
# SITE_HOST is used for generating links for the search index. (If you leave this blank it should work using relative paths.) # SITE_HOST is used for generating links for the search index. (If you leave this blank it should work using relative paths.)
SITE_HOST="localhost" SITE_HOST="${APP_HTTP_HOST}"
# SITE_URL is used for generating links for the search index. (If you leave this blank it should work using relative paths.) # SITE_URL is used for generating links for the search index. (If you leave this blank it should work using relative paths.)
SITE_URL="https://${SITE_HOST}" SITE_URL="${APP_HTTP_URL}"
# WELCOME_MSG is used for the homepage instead of "Welcome to ${SITE_NAME}!" # WELCOME_MESSAGE is used for the homepage instead of "Welcome to ${SITE_NAME}!"
WELCOME_MSG="Devel' It Up, Developer!" SITE_WELCOME_MESSAGE="Devel' It Up, Developer!"
# APP_HTTP_LISTEN_PORT is the TCP port used to access the Node application's HTTP interface (usually by a reverse proxy). # PUBLIC_DIR is the relative path to the directory to this project root for the public files
APP_HTTP_HOST="nm3clol" PUBLIC_DIR="../nm3clol-public"
# APP_HTTP_LISTEN_PORT is the TCP port used to access the Node application's HTTP interface (usually by a reverse proxy). # PAGES_DIR is the relative path to the directory to this project root for the pages rather than public files
APP_HTTP_LISTEN_PORT=3000 PAGES_DIR="pages"
# APP_URL is the URL used to access the Node application (usually by a reverse proxy). # STATIC_DIR is the relative path to the directory to this project root for the static asset files
APP_HTTP_URL="http://${APP_HTTP_HOST}:${APP_HTTP_LISTEN_PORT}" STATIC_DIR="static"
# SOLR_DOCS_HOST is the host for Apache Solr's core for indexed documents. # SOLR_DOCS_HOST is the host for Apache Solr's core for indexed documents.
SOLR_DOCS_HOST="solr" SOLR_DOCS_HOST="solr"
@ -40,6 +47,3 @@ TIKA_HOST="tika"
TIKA_PORT=9998 TIKA_PORT=9998
# TIKA_URL is the URL to access the host running Apache Tika. # TIKA_URL is the URL to access the host running Apache Tika.
TIKA_URL="http://${TIKA_HOST}:${TIKA_PORT}" TIKA_URL="http://${TIKA_HOST}:${TIKA_PORT}"
# PUBLIC_DIR is the directory relative to this project root for the public files
PUBLIC_DIR="../nm3clol-public"

51
app/config.js Normal file
View File

@ -0,0 +1,51 @@
console.log(`Configuring .env and expanding .env to include environment variable references.`);
const path = require('path');
const dotenv = require('dotenv');
const dotenvExpand = require('dotenv-expand');
let env = dotenv.config();
dotenvExpand.expand(env);
const getAppHttpHost = () => env.APP_HTTP_HOST||'nm3clol-express-app';
const getAppHttpPort = () => parseInt(env.APP_HTTP_PORT||env.PORT||3000);
const getAppHttpUrl = () => {
return env.APP_HTTP_URL || `http://${getAppHttpHost() + ((getAppHttpPort() === 80) ? '' : ':' + getAppHttpPort())}`
};
const getSiteName = () => env.SITE_NAME||"(dev) No Moss 3 Carbo Landfill Online Localhost";
const getSiteWelcomeMessage = () => env.SITE_WELCOME_MESSAGE||"Devel' It Up, Developer!";
const getSiteHost = () => env.SITE_HOST||"localhost";
const getSiteUrl = () => env.SITE_URL||getAppHttpUrl();
const getPublicPath = () => path.join(__dirname, '..', (env.PUBLIC_DIR||path.join('..', 'nm3clol-public')).replaceAll('\\', path.sep).replaceAll('/', path.sep));
const getPagesPath = () => path.join(__dirname, '..', (env.PAGES_DIR||'pages').replaceAll('\\', path.sep).replaceAll('/', path.sep));
const getStaticPath = () => path.join(__dirname, '..', (env.STATIC_DIR||'static').replaceAll('\\', path.sep).replaceAll('/', path.sep));
const getSolrDocsHost = () => env.SOLR_DOCS_HOST||'solr';
const getSolrDocsPort = () => parseInt(env.SOLR_DOCS_PORT||8983);
const getSolrDocsCore = () => env.SOLR_DOCS_CORE||'nm3clol_core';
const getSolrLawHost = () => env.SOLR_LAW_HOST||getSolrDocsHost();
const getSolrLawPort = () => parseInt(env.SOLR_LAW_PORT||getSolrDocsPort());
const getSolrLawCore = () => env.SOLR_LAW_CORE||'vacode_core';
module.exports = {
config: {
publicPath: getPublicPath(),
pagesPath: getPagesPath(),
staticPath: getStaticPath(),
siteName: getSiteName(),
siteWelcomeMessage: getSiteWelcomeMessage(),
siteHost: getSiteHost(),
siteUrl: getSiteUrl(),
appHttpHost: getAppHttpHost(),
appHttpPort: getAppHttpPort(),
appHttpUrl: getAppHttpUrl(),
solrDocsHost: getSolrDocsHost(),
solrDocsPort: getSolrDocsPort(),
solrDocsCore: getSolrDocsCore(),
solrLawHost: getSolrLawHost(),
solrLawPort: getSolrLawPort(),
solrLawCore: getSolrLawCore(),
}
};

View File

@ -1,9 +1,11 @@
console.log(`Loading nm3clol-express-app search router module...`);
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
const { parse, toString } = require('lucene'); const { parse, toString } = require('lucene');
const { createClient, Query } = require('solr-client'); const { createClient, Query } = require('solr-client');
const solrConfig = { host: process.env.SOLR_DOCS_HOST||'solr', port: process.env.SOLR_DOCS_PORT||8983, core: process.env.SOLR_DOCS_CORE_NAME||'nm3clol_core' }; const { config } = require('../config');
const helpers = require('../views/helpers/functions'); const helpers = require('../../views/helpers/functions');
router.get('/', (req, res) => { router.get('/', (req, res) => {
// Extract paging parameters from request query parameters // Extract paging parameters from request query parameters
@ -37,7 +39,7 @@ router.get('/', (req, res) => {
usePhraseHighlighter: true, usePhraseHighlighter: true,
}}); }});
// Create a Solr client // Create a Solr client
const solrClient = createClient({ host: 'solr.services.cleveland.daball.me', port: 8983, core: 'my_core' }); const solrClient = createClient({ host: config.solrDocsHost, port: config.solrDocsPort, core: config.solrDocsCore });
solrClient.search(solrQuery) solrClient.search(solrQuery)
.then(solrResponse => { .then(solrResponse => {
//console.log(require('util').inspect(solrResponse, { showHidden: true, depth: null, colors: true })); //console.log(require('util').inspect(solrResponse, { showHidden: true, depth: null, colors: true }));

View File

@ -1,3 +1,5 @@
console.log(`Starting up nm3clol-express-app...`);
const express = require('express'); const express = require('express');
const axios = require('axios'); const axios = require('axios');
const app = express(); const app = express();
@ -6,18 +8,13 @@ const path = require('path');
const glob = require('glob'); const glob = require('glob');
const matter = require('gray-matter'); const matter = require('gray-matter');
const ejs = require('ejs'); const ejs = require('ejs');
const { config } = require('./config');
const helpers = require('../views/helpers/functions'); const helpers = require('../views/helpers/functions');
const search = require('../routes/search'); const search = require('./routes/search');
const fs = require('fs'); const fs = require('fs');
const dotenv = require('dotenv');
const dotenvExpand = require('dotenv-expand');
dotenv.config();
dotenvExpand.expand(process.env);
// const advancedSearch = require('../routes/advanced-search'); // const advancedSearch = require('../routes/advanced-search');
const publicPath = path.join(__dirname, '..', process.env.PUBLIC_DIR.replaceAll('\\', path.sep).replaceAll('/', path.sep));
// Port number for HTTP server console.log(`Running app configuration:`, config);
const port = process.env.PORT||3000;
// Set EJS as the view engine // Set EJS as the view engine
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
@ -41,14 +38,14 @@ app.use(express.json());
// res.send('Hello World!'); // res.send('Hello World!');
// }) // })
console.log("Setting route for /ads.txt"); // console.log("Setting route for /ads.txt");
app.get('/ads.txt', (req, res) => { // app.get('/ads.txt', (req, res) => {
res.setHeader("Content-Type", "text/plain"); // res.setHeader("Content-Type", "text/plain");
res.setHeader("Cache-Control", "no-cache"); // res.setHeader("Cache-Control", "no-cache");
res.send(`google.com, pub-8937572456576531, DIRECT, f08c47fec0942fa0`); // res.send(`google.com, pub-8937572456576531, DIRECT, f08c47fec0942fa0`);
}); // });
console.log("Setting route for /robots.txt"); console.log(`Serving /robots.txt from memory.`);
app.get('/robots.txt', (req, res) => { app.get('/robots.txt', (req, res) => {
res.setHeader("Content-Type", "text/plain"); res.setHeader("Content-Type", "text/plain");
res.setHeader("Cache-Control", "no-cache"); res.setHeader("Cache-Control", "no-cache");
@ -63,25 +60,29 @@ Allow: /
}); });
// Search endpoints // Search endpoints
console.log("Setting routes for /search"); console.log(`Serving /search using search router.`);
app.use('/search', search.router); app.use('/search', search.router);
// app.use('/advanced-search', advancedSearch.router); // app.use('/advanced-search', advancedSearch.router);
// Endpoints for all the site's pages. // Endpoints for all the site's pages.
console.log("Scanning for pages to create routes"); console.log(`Scanning for pages in ${config.pagesPath} to create routes.`);
glob.globSync('pages/**/*.md', { glob.globSync('**/*.md', {
cwd: path.join(__dirname, '..'), cwd: config.pagesPath,
matchBase: true, matchBase: true,
follow: true, follow: true,
}).forEach((filePath) => { }).forEach((filePath) => {
const expressRoutePathFromFilePath = (filePath) => { const expressRoutePathFromFilePath = (filePath) => {
return filePath.substring('pages'.length, filePath.length - path.extname(filePath).length).replaceAll(path.sep, path.posix.sep); filePath = filePath.substring(0, filePath.length - path.extname(filePath).length).replaceAll(path.sep, path.posix.sep);
if (!filePath.startsWith('/') && filePath.length > 0) {
filePath = `/${filePath}`;
}
return filePath;
}; };
const route = expressRoutePathFromFilePath(filePath); const route = expressRoutePathFromFilePath(filePath);
const fullFilePath = path.join(__dirname, '..', filePath); const fullFilePath = path.join(config.pagesPath, filePath);
let paths = route.split(path.posix.sep); let paths = route.split(path.posix.sep);
paths[0] = 'public'; paths[0] = 'public';
console.log(`Setting route for ${route}`); console.log(`Serving ${route} route as a page at ${fullFilePath}.`);
app.get(route, async (req, res) => { app.get(route, async (req, res) => {
const fm = matter.read(fullFilePath); const fm = matter.read(fullFilePath);
const fmData = { fm: fm.data, excerpt: fm.excerpt }; const fmData = { fm: fm.data, excerpt: fm.excerpt };
@ -91,7 +92,7 @@ glob.globSync('pages/**/*.md', {
}); });
}); });
// console.log("Scanning for documents to create routes"); // console.log("Scanning for documents to create routes.");
// glob.globSync('**/*{.pdf,.docx,.xlsx,.pptx,.doc,.xls,.ppt}', { // glob.globSync('**/*{.pdf,.docx,.xlsx,.pptx,.doc,.xls,.ppt}', {
// cwd: path.join(__dirname, '..', 'public'), // cwd: path.join(__dirname, '..', 'public'),
// matchBase: true, // matchBase: true,
@ -114,7 +115,8 @@ glob.globSync('pages/**/*.md', {
// }); // });
// }); // });
console.log("Scanning for web archive HTML documents to create routes"); //TODO: Rewrite this facility so that it utilizes Git index as a filesystem.
console.log("Scanning for web archive HTML documents to create routes.");
glob.globSync('Web_Site_Archives/**/*{.htm,.html}', { glob.globSync('Web_Site_Archives/**/*{.htm,.html}', {
cwd: path.join(__dirname, '..', 'public'), cwd: path.join(__dirname, '..', 'public'),
matchBase: true, matchBase: true,
@ -136,8 +138,8 @@ glob.globSync('Web_Site_Archives/**/*{.htm,.html}', {
}); });
// Endpoints for all the site's YouTube videos. //TODO: Rewrite this facility so that it utilizes Git index as a filesystem.
console.log("Scanning for archived videos to create routes"); console.log("Scanning for archived videos to create routes.");
glob.globSync(['Russell_County/Board_of_Supervisors/YouTube_Archive/**/*.info.json', 'Virginia_Energy/YouTube_Archive/**/*.info.json', 'Virginia_Governor/**/*.info.json'], { glob.globSync(['Russell_County/Board_of_Supervisors/YouTube_Archive/**/*.info.json', 'Virginia_Energy/YouTube_Archive/**/*.info.json', 'Virginia_Governor/**/*.info.json'], {
cwd: path.join(__dirname, '..', 'public'), cwd: path.join(__dirname, '..', 'public'),
matchBase: true, matchBase: true,
@ -187,10 +189,10 @@ glob.globSync(['Russell_County/Board_of_Supervisors/YouTube_Archive/**/*.info.js
//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('/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'));
console.log(`Setting routes for /vendor/**/*`);; console.log(`Serving /vendor/**/* route for all files in ${path.join(config.staticPath, 'vendor')}`);;
app.get('/vendor/**/*', async (req, res) => { app.get('/vendor/**/*', async (req, res) => {
await serve(req, res, { await serve(req, res, {
public: path.join(__dirname, '..', 'static'), public: config.staticPath,
symlinks: true, symlinks: true,
trailingSlash: true, trailingSlash: true,
cleanUrls: false, cleanUrls: false,
@ -204,10 +206,10 @@ app.get('/vendor/**/*', async (req, res) => {
}); });
}); });
console.log(`Setting routes for /css/*.css`);; console.log(`Serving /css/*.css route for all files in ${path.join(config.staticPath, 'css')}`);;
app.get('/css/*.css', async (req, res) => { app.get('/css/*.css', async (req, res) => {
await serve(req, res, { await serve(req, res, {
public: path.join(__dirname, '..', 'static'), public: config.staticPath,
symlinks: true, symlinks: true,
trailingSlash: true, trailingSlash: true,
cleanUrls: false, cleanUrls: false,
@ -221,10 +223,10 @@ app.get('/css/*.css', async (req, res) => {
}); });
}); });
console.log(`Setting routes for /svg/*.svg`);; console.log(`Serving /svg/*.svg route for all files in ${path.join(config.staticPath, 'svg')}`);;
app.get('/svg/*.svg', async (req, res) => { app.get('/svg/*.svg', async (req, res) => {
await serve(req, res, { await serve(req, res, {
public: path.join(__dirname, '..', 'static'), public: config.staticPath,
symlinks: true, symlinks: true,
trailingSlash: true, trailingSlash: true,
cleanUrls: false, cleanUrls: false,
@ -238,10 +240,11 @@ app.get('/svg/*.svg', async (req, res) => {
}); });
}); });
console.log(`Setting route for *`); //TODO: Rewrite this facility so that it utilizes Git index as a filesystem.
console.log(`Serving * default route for all files in ${config.publicPath}`);;
app.get('*', async (req, res) => { app.get('*', async (req, res) => {
await serve(req, res, { await serve(req, res, {
public: publicPath, public: config.publicPath,
symlinks: true, symlinks: true,
trailingSlash: true, trailingSlash: true,
cleanUrls: false, cleanUrls: false,
@ -276,6 +279,11 @@ app.get('*', async (req, res) => {
}); });
// Start server // Start server
app.listen(port, () => { app.listen(config.appHttpPort, () => {
console.log(`no-moss-3-carbo-landfill-library.online app listening on port ${port}`); console.log(`nm3clol-express-app HTTP server listening on port ${config.appHttpPort}.`)
console.log(`To access your app, you can use the localhost URL, http://localhost:${config.appHttpPort}.`);
console.log(`To access your app, you can use the 127.0.0.1 host, http://127.0.0.1:${config.appHttpPort}.`);
console.log(`To access your app, you can use the ::1 host, http://[::1]:${config.appHttpPort}.`);
console.log(`To access your app, you might can use the app host name, ${config.appHttpUrl}.`);
console.log(`This app is configured to use the web site URL, ${config.siteUrl}.`);
}); });

View File

@ -18,9 +18,9 @@
<% if (h.shouldShowDirectorySeparator({index})) { %> <% if (h.shouldShowDirectorySeparator({index})) { %>
<span class="separator">&rsaquo; </span> <span class="separator">&rsaquo; </span>
<% } %> <% } %>
<% if (h.shouldShowWelcomeBanner({paths})) { %> <% if (h.shouldShowSiteWelcomeMessage({paths})) { %>
<i>&nbsp;</i> <i>&nbsp;</i>
<%= h.getWelcomeBanner() %> <%= h.getSiteWelcomeMessage() %>
<% } else if (h.shouldOmitLinkOnLastBreadcrumb({paths, index})) { %> <% } else if (h.shouldOmitLinkOnLastBreadcrumb({paths, index})) { %>
<%= h.trimSlashes({path: value.name}).replaceAll('_', ' ') %> <%= h.trimSlashes({path: value.name}).replaceAll('_', ' ') %>
<% } else { %> <% } else { %>

View File

@ -1,7 +1,7 @@
const path = require('path'); const path = require('path');
const glob = require('glob'); const glob = require('glob');
const fs = require('fs'); const fs = require('fs');
const process = require('process'); const config = require('../../app/config');
const markdownit = require('markdown-it'); const markdownit = require('markdown-it');
var markdownItAttrs = require('markdown-it-attrs'); var markdownItAttrs = require('markdown-it-attrs');
const md = markdownit({ const md = markdownit({
@ -18,9 +18,7 @@ const md = markdownit({
); );
const moment = require('moment-timezone').tz.setDefault("UTC"); const moment = require('moment-timezone').tz.setDefault("UTC");
const getSiteName = () => { const getSiteName = config.getSiteName;
return process.env.SITE_NAME || '(dev) No Moss 3 Carbo Landfill Online Localhost';
}
const trimSlashes = ({path}) => { const trimSlashes = ({path}) => {
return path.replace(/^[\/\\]|[\/\\]$/g, ''); return path.replace(/^[\/\\]|[\/\\]$/g, '');
@ -40,11 +38,9 @@ const getDirectoryTitle = ({directory}) => {
.join(' - '); .join(' - ');
return (directory=="public") ? getSiteName() : `${title} - ${getSiteName()}`; return (directory=="public") ? getSiteName() : `${title} - ${getSiteName()}`;
}; };
const getWelcomeBanner = () => { const getSiteWelcomeMessage = config.getSiteWelcomeMessage;
return process.env.WELCOME_MSG || `Welcome to ${getSiteName()}!`;
};
const shouldShowDirectorySeparator = ({index}) => (index > 0); const shouldShowDirectorySeparator = ({index}) => (index > 0);
const shouldShowWelcomeBanner = ({paths}) => (paths.length == 1); const shouldShowSiteWelcomeMessage = ({paths}) => (paths.length == 1);
const shouldOmitLinkOnLastBreadcrumb = ({paths, index}) => (index == paths.length-1); const shouldOmitLinkOnLastBreadcrumb = ({paths, index}) => (index == paths.length-1);
const resolveReadmeFile = ({directory}) => { const resolveReadmeFile = ({directory}) => {
@ -88,7 +84,7 @@ const renderArchive = (html, paths) => {
// Header and Footer content // Header and Footer content
const headHeaderContent = ``; const headHeaderContent = ``;
const headFooterContent = ` const headFooterContent = `
<!-- Dynamically Inserted Code by ${process.env.SITE_NAME||'No Moss 3 Carbo Landfill Online Library'} --> <!-- Dynamically Inserted Code by ${getSiteName()} -->
<style> <style>
.__archived__content__, .__archived__content__ p { background-color: #f44336; color: #fff; font-size: 12pt; font-family: "Noto Serif", Times, "Times New Roman", serif; text-align: center; } .__archived__content__, .__archived__content__ p { background-color: #f44336; color: #fff; font-size: 12pt; font-family: "Noto Serif", Times, "Times New Roman", serif; text-align: center; }
.__archived__content__ h1 { font-family: "Cinzel Decorative", Verdana, Arial, sans-serif; color: #fff; font-weight: 700; font-size: 18pt; text-align: center; } .__archived__content__ h1 { font-family: "Cinzel Decorative", Verdana, Arial, sans-serif; color: #fff; font-weight: 700; font-size: 18pt; text-align: center; }
@ -124,9 +120,9 @@ const renderArchive = (html, paths) => {
<!-- End dynamically inserted code --> <!-- End dynamically inserted code -->
`; `;
const bodyHeaderContent = ` const bodyHeaderContent = `
<!-- Dynamically Inserted Code by ${process.env.SITE_NAME||'No Moss 3 Carbo Landfill Online Library'} --> <!-- Dynamically Inserted Code by ${getSiteName()} -->
<div class="__archived__content__"> <div class="__archived__content__">
<h1>${process.env.SITE_NAME||'No Moss 3 Carbo Landfill Online Library'}</h1> <h1>${getSiteName()}</h1>
<h2>Archived Web Site</h2> <h2>Archived Web Site</h2>
<p> <p>
This is an archived version of the original website. Online features will not be functional. Do not submit any personal information to this archive. This is an archived version of the original website. Online features will not be functional. Do not submit any personal information to this archive.
@ -138,7 +134,7 @@ const renderArchive = (html, paths) => {
<!-- End dynamically inserted code --> <!-- End dynamically inserted code -->
`; `;
const bodyFooterContent = ` const bodyFooterContent = `
<!-- Dynamically Inserted Code by ${process.env.SITE_NAME||'No Moss 3 Carbo Landfill Online Library'} --> <!-- Dynamically Inserted Code by ${getSiteName()} -->
<div class="__archived__content__"> <div class="__archived__content__">
<p> <p>
This is an archived version of the original website. Online features will not be functional. Do not submit any personal information to this archive. This is an archived version of the original website. Online features will not be functional. Do not submit any personal information to this archive.
@ -165,9 +161,9 @@ module.exports = {
getSiteName, getSiteName,
getDirectoryName, getDirectoryName,
getDirectoryTitle, getDirectoryTitle,
getWelcomeBanner, getSiteWelcomeMessage,
shouldShowDirectorySeparator, shouldShowDirectorySeparator,
shouldShowWelcomeBanner, shouldShowSiteWelcomeMessage,
shouldOmitLinkOnLastBreadcrumb, shouldOmitLinkOnLastBreadcrumb,
directoryContainsReadme, directoryContainsReadme,
printReadme, printReadme,