From 6099ffece134bcde9197c1be51a0b23f885465b5 Mon Sep 17 00:00:00 2001 From: Spappz <34202141+Spappz@users.noreply.github.com> Date: Sat, 25 Jan 2025 20:29:31 +0000 Subject: [PATCH] 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 --- server.js | 18 ++++++++++++------ src/middleware/basicAuth.js | 5 ++--- src/middleware/whitelist.js | 6 ++++-- src/util.js | 11 +++++++++++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/server.js b/server.js index bc2a884c2..cbd161847 100644 --- a/server.js +++ b/server.js @@ -67,6 +67,7 @@ import { forwardFetchResponse, removeColorFormatting, getSeparator, + safeReadFileSync, } from './src/util.js'; import { UPLOADS_DIRECTORY } from './src/constants.js'; import { ensureThumbnailCache } from './src/endpoints/thumbnails.js'; @@ -615,12 +616,6 @@ app.use('/api/backends/scale-alt', scaleAltRouter); app.use('/api/speech', speechRouter); app.use('/api/azure', azureRouter); -// If all other middlewares fail, send 404 error. -const notFoundWebpage = fs.readFileSync('./public/error/url-not-found.html', { encoding: 'utf-8' }); -app.use((req, res, next) => { - res.status(404).send(notFoundWebpage); -}); - const tavernUrlV6 = new URL( (cliArguments.ssl ? 'https://' : 'http://') + (listen ? '[::]' : '[::1]') + @@ -927,6 +922,16 @@ async function verifySecuritySettings() { } } +/** + * Registers a not-found error response if a not-found error page exists. Should only be called after all other middlewares have been registered. + */ +function apply404Middleware() { + const notFoundWebpage = safeReadFileSync('./public/error/url-not-found.html') ?? ''; + app.use((req, res) => { + res.status(404).send(notFoundWebpage); + }); +} + // User storage module needs to be initialized before starting the server initUserStorage(dataRoot) .then(ensurePublicDirectoriesExist) @@ -934,4 +939,5 @@ initUserStorage(dataRoot) .then(migrateSystemPrompts) .then(verifySecuritySettings) .then(preSetupTasks) + .then(apply404Middleware) .finally(startServer); diff --git a/src/middleware/basicAuth.js b/src/middleware/basicAuth.js index 542aad634..4548a3f20 100644 --- a/src/middleware/basicAuth.js +++ b/src/middleware/basicAuth.js @@ -5,13 +5,12 @@ import { Buffer } from 'node:buffer'; import storage from 'node-persist'; import { getAllUserHandles, toKey, getPasswordHash } from '../users.js'; -import { getConfig, getConfigValue } from '../util.js'; -import { readFileSync } from 'node:fs'; +import { getConfig, getConfigValue, safeReadFileSync } from '../util.js'; const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false); const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false); -const unauthorizedWebpage = readFileSync('./public/error/unauthorized.html', { encoding: 'utf-8' }); +const unauthorizedWebpage = safeReadFileSync('./public/error/unauthorized.html') ?? ''; const unauthorizedResponse = (res) => { res.set('WWW-Authenticate', 'Basic realm="SillyTavern", charset="UTF-8"'); return res.status(401).send(unauthorizedWebpage); diff --git a/src/middleware/whitelist.js b/src/middleware/whitelist.js index 6edf5a75a..2922c42b1 100644 --- a/src/middleware/whitelist.js +++ b/src/middleware/whitelist.js @@ -5,13 +5,15 @@ import Handlebars from 'handlebars'; import ipMatching from 'ip-matching'; import { getIpFromRequest } from '../express-common.js'; -import { color, getConfigValue } from '../util.js'; +import { color, getConfigValue, safeReadFileSync } from '../util.js'; const whitelistPath = path.join(process.cwd(), './whitelist.txt'); const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false); let whitelist = getConfigValue('whitelist', []); let knownIPs = new Set(); -const forbiddenWebpage = Handlebars.compile(fs.readFileSync('./public/error/forbidden-by-whitelist.html', 'utf-8')); +const forbiddenWebpage = Handlebars.compile( + safeReadFileSync('./public/error/forbidden-by-whitelist.html') ?? '', +); if (fs.existsSync(whitelistPath)) { try { diff --git a/src/util.js b/src/util.js index 0fe03e76c..19bd3ccc7 100644 --- a/src/util.js +++ b/src/util.js @@ -871,3 +871,14 @@ export class MemoryLimitedMap { return this.map[Symbol.iterator](); } } + +/** + * A 'safe' version of `fs.readFileSync()`. Returns the contents of a file if it exists, falling back to a default value if not. + * @param {string} filePath Path of the file to be read. + * @param {Parameters[1]} options Options object to pass through to `fs.readFileSync()` (default: `{ encoding: 'utf-8' }`). + * @returns The contents at `filePath` if it exists, or `null` if not. + */ +export function safeReadFileSync(filePath, options = { encoding: 'utf-8' }) { + if (fs.existsSync(filePath)) return fs.readFileSync(filePath, options); + return null; +}