No exceptions on missing error webpages - Create a `safeReadFileSync()` function in `src/utils.js` to wrap around `fs.readFileSync()` - Migrate error-webpage loads to use `safeReadFileSync()`, with default values of an empty string - Move the 404 error middleware to explicitly only be called *after* extensions are registered

6099ffece134bcde9197c1be51a0b23f885465b5

Spappz <34202141+Spappz@users.noreply.github.com>

Signed
4 files changed, +29 -11Showing whitespace changes
server.js+12 -6
@@ -67,6 +67,7 @@ import {
6767 forwardFetchResponse,
6868 removeColorFormatting,
6969 getSeparator,
70+ safeReadFileSync,
7071} from './src/util.js';
7172import { UPLOADS_DIRECTORY } from './src/constants.js';
7273import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';
@@ -615,12 +616,6 @@ app.use('/api/backends/scale-alt', scaleAltRouter);
615616app.use('/api/speech', speechRouter);
616617app.use('/api/azure', azureRouter);
617618
618-// If all other middlewares fail, send 404 error.
619-const notFoundWebpage = fs.readFileSync('./public/error/url-not-found.html', { encoding: 'utf-8' });
620-app.use((req, res, next) => {
621- res.status(404).send(notFoundWebpage);
622-});
623-
624619const tavernUrlV6 = new URL(
625620 (cliArguments.ssl ? 'https://' : 'http://') +
626621 (listen ? '[::]' : '[::1]') +
@@ -927,6 +922,16 @@ async function verifySecuritySettings() {
927922 }
928923}
929924
925+/**
926+ * Registers a not-found error response if a not-found error page exists. Should only be called after all other middlewares have been registered.
927+ */
928+function apply404Middleware() {
929+ const notFoundWebpage = safeReadFileSync('./public/error/url-not-found.html') ?? '';
930+ app.use((req, res) => {
931+ res.status(404).send(notFoundWebpage);
932+ });
933+}
934+
930935// User storage module needs to be initialized before starting the server
931936initUserStorage(dataRoot)
932937 .then(ensurePublicDirectoriesExist)
@@ -934,4 +939,5 @@ initUserStorage(dataRoot)
934939 .then(migrateSystemPrompts)
935940 .then(verifySecuritySettings)
936941 .then(preSetupTasks)
942+ .then(apply404Middleware)
937943 .finally(startServer);
src/middleware/basicAuth.js+2 -3
@@ -5,13 +5,12 @@
55import { Buffer } from 'node:buffer';
66import storage from 'node-persist';
77import { getAllUserHandles, toKey, getPasswordHash } from '../users.js';
88import { getConfig, getConfigValue, safeReadFileSync } from '../util.js';
9-import { readFileSync } from 'node:fs';
109
1110const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false);
1211const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);
1312
1413const unauthorizedWebpage = readFileSyncsafeReadFileSync('./public/error/unauthorized.html', {) encoding:?? 'utf-8' });
1514const unauthorizedResponse = (res) => {
1615 res.set('WWW-Authenticate', 'Basic realm="SillyTavern", charset="UTF-8"');
1716 return res.status(401).send(unauthorizedWebpage);
src/middleware/whitelist.js+4 -2
@@ -5,13 +5,15 @@ import Handlebars from 'handlebars';
55import ipMatching from 'ip-matching';
66
77import { getIpFromRequest } from '../express-common.js';
88import { color, getConfigValue, safeReadFileSync } from '../util.js';
99
1010const whitelistPath = path.join(process.cwd(), './whitelist.txt');
1111const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false);
1212let whitelist = getConfigValue('whitelist', []);
1313let knownIPs = new Set();
14-const forbiddenWebpage = Handlebars.compile(fs.readFileSync('./public/error/forbidden-by-whitelist.html', 'utf-8'));
14+const forbiddenWebpage = Handlebars.compile(
15+ safeReadFileSync('./public/error/forbidden-by-whitelist.html') ?? '',
16+);
1517
1618if (fs.existsSync(whitelistPath)) {
1719 try {
src/util.js+11 -0
@@ -871,3 +871,14 @@ export class MemoryLimitedMap {
871871 return this.map[Symbol.iterator]();
872872 }
873873}
874+
875+/**
876+ * A 'safe' version of `fs.readFileSync()`. Returns the contents of a file if it exists, falling back to a default value if not.
877+ * @param {string} filePath Path of the file to be read.
878+ * @param {Parameters<typeof fs.readFileSync>[1]} options Options object to pass through to `fs.readFileSync()` (default: `{ encoding: 'utf-8' }`).
879+ * @returns The contents at `filePath` if it exists, or `null` if not.
880+ */
881+export function safeReadFileSync(filePath, options = { encoding: 'utf-8' }) {
882+ if (fs.existsSync(filePath)) return fs.readFileSync(filePath, options);
883+ return null;
884+}