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.
# 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="(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="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="https://${SITE_HOST}"
# WELCOME_MSG is used for the homepage instead of "Welcome to ${SITE_NAME}!"
WELCOME_MSG="Devel' It Up, Developer!"
SITE_URL="${APP_HTTP_URL}"
# WELCOME_MESSAGE is used for the homepage instead of "Welcome to ${SITE_NAME}!"
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).
APP_HTTP_HOST="nm3clol"
# APP_HTTP_LISTEN_PORT is the TCP port used to access the Node application's HTTP interface (usually by a reverse proxy).
APP_HTTP_LISTEN_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_LISTEN_PORT}"
# PUBLIC_DIR is the relative path to the directory to this project root for the public files
PUBLIC_DIR="../nm3clol-public"
# PAGES_DIR is the relative path to the directory to this project root for the pages rather than public files
PAGES_DIR="pages"
# STATIC_DIR is the relative path to the directory to this project root for the static asset files
STATIC_DIR="static"
# SOLR_DOCS_HOST is the host for Apache Solr's core for indexed documents.
SOLR_DOCS_HOST="solr"
@ -40,6 +47,3 @@ TIKA_HOST="tika"
TIKA_PORT=9998
# TIKA_URL is the URL to access the host running Apache Tika.
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 router = express.Router();
const { parse, toString } = require('lucene');
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 helpers = require('../views/helpers/functions');
const { config } = require('../config');
const helpers = require('../../views/helpers/functions');
router.get('/', (req, res) => {
// Extract paging parameters from request query parameters
@ -37,7 +39,7 @@ router.get('/', (req, res) => {
usePhraseHighlighter: true,
}});
// 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)
.then(solrResponse => {
//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 axios = require('axios');
const app = express();
@ -6,18 +8,13 @@ const path = require('path');
const glob = require('glob');
const matter = require('gray-matter');
const ejs = require('ejs');
const { config } = require('./config');
const helpers = require('../views/helpers/functions');
const search = require('../routes/search');
const search = require('./routes/search');
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 publicPath = path.join(__dirname, '..', process.env.PUBLIC_DIR.replaceAll('\\', path.sep).replaceAll('/', path.sep));
// Port number for HTTP server
const port = process.env.PORT||3000;
console.log(`Running app configuration:`, config);
// Set EJS as the view engine
app.set('view engine', 'ejs');
@ -41,14 +38,14 @@ app.use(express.json());
// res.send('Hello World!');
// })
console.log("Setting route for /ads.txt");
app.get('/ads.txt', (req, res) => {
res.setHeader("Content-Type", "text/plain");
res.setHeader("Cache-Control", "no-cache");
res.send(`google.com, pub-8937572456576531, DIRECT, f08c47fec0942fa0`);
});
// console.log("Setting route for /ads.txt");
// app.get('/ads.txt', (req, res) => {
// res.setHeader("Content-Type", "text/plain");
// res.setHeader("Cache-Control", "no-cache");
// 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) => {
res.setHeader("Content-Type", "text/plain");
res.setHeader("Cache-Control", "no-cache");
@ -63,25 +60,29 @@ Allow: /
});
// Search endpoints
console.log("Setting routes for /search");
console.log(`Serving /search using search router.`);
app.use('/search', search.router);
// app.use('/advanced-search', advancedSearch.router);
// Endpoints for all the site's pages.
console.log("Scanning for pages to create routes");
glob.globSync('pages/**/*.md', {
cwd: path.join(__dirname, '..'),
console.log(`Scanning for pages in ${config.pagesPath} to create routes.`);
glob.globSync('**/*.md', {
cwd: config.pagesPath,
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);
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 fullFilePath = path.join(__dirname, '..', filePath);
const fullFilePath = path.join(config.pagesPath, filePath);
let paths = route.split(path.posix.sep);
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) => {
const fm = matter.read(fullFilePath);
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}', {
// cwd: path.join(__dirname, '..', 'public'),
// 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}', {
cwd: path.join(__dirname, '..', 'public'),
matchBase: true,
@ -136,8 +138,8 @@ glob.globSync('Web_Site_Archives/**/*{.htm,.html}', {
});
// Endpoints for all the site's YouTube videos.
console.log("Scanning for archived videos to create routes");
//TODO: Rewrite this facility so that it utilizes Git index as a filesystem.
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'], {
cwd: path.join(__dirname, '..', 'public'),
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'));
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) => {
await serve(req, res, {
public: path.join(__dirname, '..', 'static'),
public: config.staticPath,
symlinks: true,
trailingSlash: true,
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) => {
await serve(req, res, {
public: path.join(__dirname, '..', 'static'),
public: config.staticPath,
symlinks: true,
trailingSlash: true,
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) => {
await serve(req, res, {
public: path.join(__dirname, '..', 'static'),
public: config.staticPath,
symlinks: true,
trailingSlash: true,
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) => {
await serve(req, res, {
public: publicPath,
public: config.publicPath,
symlinks: true,
trailingSlash: true,
cleanUrls: false,
@ -276,6 +279,11 @@ app.get('*', async (req, res) => {
});
// Start server
app.listen(port, () => {
console.log(`no-moss-3-carbo-landfill-library.online app listening on port ${port}`);
app.listen(config.appHttpPort, () => {
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})) { %>
<span class="separator">&rsaquo; </span>
<% } %>
<% if (h.shouldShowWelcomeBanner({paths})) { %>
<% if (h.shouldShowSiteWelcomeMessage({paths})) { %>
<i>&nbsp;</i>
<%= h.getWelcomeBanner() %>
<%= h.getSiteWelcomeMessage() %>
<% } else if (h.shouldOmitLinkOnLastBreadcrumb({paths, index})) { %>
<%= h.trimSlashes({path: value.name}).replaceAll('_', ' ') %>
<% } else { %>

View File

@ -1,7 +1,7 @@
const path = require('path');
const glob = require('glob');
const fs = require('fs');
const process = require('process');
const config = require('../../app/config');
const markdownit = require('markdown-it');
var markdownItAttrs = require('markdown-it-attrs');
const md = markdownit({
@ -18,9 +18,7 @@ const md = markdownit({
);
const moment = require('moment-timezone').tz.setDefault("UTC");
const getSiteName = () => {
return process.env.SITE_NAME || '(dev) No Moss 3 Carbo Landfill Online Localhost';
}
const getSiteName = config.getSiteName;
const trimSlashes = ({path}) => {
return path.replace(/^[\/\\]|[\/\\]$/g, '');
@ -40,11 +38,9 @@ const getDirectoryTitle = ({directory}) => {
.join(' - ');
return (directory=="public") ? getSiteName() : `${title} - ${getSiteName()}`;
};
const getWelcomeBanner = () => {
return process.env.WELCOME_MSG || `Welcome to ${getSiteName()}!`;
};
const getSiteWelcomeMessage = config.getSiteWelcomeMessage;
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 resolveReadmeFile = ({directory}) => {
@ -88,7 +84,7 @@ const renderArchive = (html, paths) => {
// Header and Footer content
const headHeaderContent = ``;
const headFooterContent = `
<!-- Dynamically Inserted Code by ${process.env.SITE_NAME||'No Moss 3 Carbo Landfill Online Library'} -->
<!-- Dynamically Inserted Code by ${getSiteName()} -->
<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__ 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 -->
`;
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__">
<h1>${process.env.SITE_NAME||'No Moss 3 Carbo Landfill Online Library'}</h1>
<h1>${getSiteName()}</h1>
<h2>Archived Web Site</h2>
<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.
@ -138,7 +134,7 @@ const renderArchive = (html, paths) => {
<!-- End dynamically inserted code -->
`;
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__">
<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.
@ -165,9 +161,9 @@ module.exports = {
getSiteName,
getDirectoryName,
getDirectoryTitle,
getWelcomeBanner,
getSiteWelcomeMessage,
shouldShowDirectorySeparator,
shouldShowWelcomeBanner,
shouldShowSiteWelcomeMessage,
shouldOmitLinkOnLastBreadcrumb,
directoryContainsReadme,
printReadme,