diff --git a/default/config.yaml b/default/config.yaml index 55ef604b6..b8c9efe12 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -4,17 +4,21 @@ dataRoot: ./data # -- SERVER CONFIGURATION -- # Listen for incoming connections listen: false -# Enables IPv6 and/or IPv4 +# Enables IPv6 and/or IPv4 protocols. Need to have at least one enabled! protocol: ipv4: true ipv6: false -# Prefers IPv6 for dns, you should probably enable this on ISPs that don't have issues with IPv6 +# Prefers IPv6 for DNS. Enable this on ISPs that don't have issues with IPv6 dnsPreferIPv6: false -# the hostname that autorun opens probably best left on auto. use options like 'localhost', 'st.example.com' +# The hostname that autorun opens. +# - Use "auto" to let the server decide +# - Use options like 'localhost', 'st.example.com' autorunHostname: "auto" # Server port port: 8000 -# overrides the port for autorun with open your browser with this port and ignore what port the server is running on. -1 is use server port +# Overrides the port for autorun in browser. +# - Use -1 to use the server port. +# - Specify a port to override the default. autorunPortOverride: -1 # -- SECURITY CONFIGURATION -- # Toggle whitelist mode diff --git a/server.js b/server.js index 238c9a715..df46364ca 100644 --- a/server.js +++ b/server.js @@ -43,6 +43,8 @@ const { getConfigValue, color, forwardFetchResponse, + removeColorFormatting, + getSeparator, } = require('./src/util'); const { ensureThumbnailCache } = require('./src/endpoints/thumbnails'); @@ -54,10 +56,6 @@ if (process.versions && process.versions.node && process.versions.node.match(/20 if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false); } - - - - const DEFAULT_PORT = 8000; const DEFAULT_AUTORUN = false; const DEFAULT_LISTEN = false; @@ -618,7 +616,6 @@ const tavernUrl = new URL( (':' + server_port), ); - /** * Tasks that need to be run before the server starts listening. */ @@ -667,19 +664,11 @@ const preSetupTasks = async function () { }); }; -function removeColorFormatting(text) { - // ANSI escape codes for colors are usually in the format \x1b[m - return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, ''); -} - -function getSeparator(n) { - return '='.repeat(n); -} - - - +/** + * Gets the hostname to use for autorun in the browser. + * @returns {string} The hostname to use for autorun + */ function getAutorunHostname() { - if (autorunHostname === 'auto') { if (enableIPv6 && enableIPv4) { if (avoidLocalhost) return '[::1]'; @@ -698,13 +687,12 @@ function getAutorunHostname() { return autorunHostname; } - /** * Tasks that need to be run after the server starts listening. + * @param {boolean} v6Failed If the server failed to start on IPv6 + * @param {boolean} v4Failed If the server failed to start on IPv4 */ const postSetupTasks = async function (v6Failed, v4Failed) { - - const autorunUrl = new URL( (cliArguments.ssl ? 'https://' : 'http://') + (getAutorunHostname()) + @@ -712,30 +700,24 @@ const postSetupTasks = async function (v6Failed, v4Failed) { ((autorunPortOverride >= 0) ? autorunPortOverride : server_port), ); - console.log('Launching...'); if (autorun) open(autorunUrl.toString()); setWindowTitle('SillyTavern WebServer'); - - let ipv6Color = color.green; - let ipv4Color = color.green; - let autorunColor = color.blue; - let logListen = 'SillyTavern is listening on'; if (enableIPv6 && !v6Failed) { - logListen += ipv6Color(' IPv6: ' + tavernUrlV6.host); + logListen += color.green(' IPv6: ' + tavernUrlV6.host); } if (enableIPv4 && !v4Failed) { - logListen += ipv4Color(' IPv4: ' + tavernUrl.host); + logListen += color.green(' IPv4: ' + tavernUrl.host); } - let goToLog = 'Go to: ' + autorunColor(autorunUrl) + ' to open SillyTavern'; - let plainGoToLog = removeColorFormatting(goToLog); + const goToLog = 'Go to: ' + color.blue(autorunUrl) + ' to open SillyTavern'; + const plainGoToLog = removeColorFormatting(goToLog); console.log(logListen); console.log('\n' + getSeparator(plainGoToLog.length) + '\n'); @@ -798,8 +780,11 @@ function logSecurityAlert(message) { process.exit(1); } - - +/** + * Handles the case where the server failed to start on one or both protocols. + * @param {boolean} v6Failed If the server failed to start on IPv6 + * @param {boolean} v4Failed If the server failed to start on IPv4 + */ function handleServerListenFail(v6Failed, v4Failed) { if (v6Failed && !enableIPv4) { console.error('fatal error: Failed to start server on IPv6 and IPv4 disabled'); @@ -817,7 +802,12 @@ function handleServerListenFail(v6Failed, v4Failed) { } } - +/** + * Creates an HTTPS server. + * @param {URL} url The URL to listen on + * @returns {Promise} A promise that resolves when the server is listening + * @throws {Error} If the server fails to start + */ function createHttpsServer(url) { return new Promise((resolve, reject) => { const server = https.createServer( @@ -831,6 +821,12 @@ function createHttpsServer(url) { }); } +/** + * Creates an HTTP server. + * @param {URL} url The URL to listen on + * @returns {Promise} A promise that resolves when the server is listening + * @throws {Error} If the server fails to start + */ function createHttpServer(url) { return new Promise((resolve, reject) => { const server = http.createServer(app); @@ -840,22 +836,16 @@ function createHttpServer(url) { }); } - - - async function startHTTPorHTTPS() { let v6Failed = false; let v4Failed = false; - let createFunc = createHttpServer; - if (cliArguments.ssl) { - createFunc = createHttpsServer; - } + const createFunc = cliArguments.ssl ? createHttpsServer : createHttpServer; if (enableIPv6) { try { await createFunc(tavernUrlV6); - } catch(error) { + } catch (error) { if (enableIPv4) { console.error('non-fatal error: failed to start server on IPv6', error); } @@ -867,7 +857,7 @@ async function startHTTPorHTTPS() { if (enableIPv4) { try { await createFunc(tavernUrl); - } catch(error) { + } catch (error) { if (enableIPv6) { console.error('non-fatal error: failed to start server on IPv4', error); } @@ -875,25 +865,17 @@ async function startHTTPorHTTPS() { v4Failed = true; } } + return [v6Failed, v4Failed]; } - - - async function startServer() { - let v6Failed = false; - let v4Failed = false; - - - [v6Failed, v4Failed] = await startHTTPorHTTPS(); + const [v6Failed, v4Failed] = await startHTTPorHTTPS(); handleServerListenFail(v6Failed, v4Failed); postSetupTasks(v6Failed, v4Failed); } - - async function verifySecuritySettings() { // Skip all security checks as listen is set to false if (!listen) { diff --git a/src/util.js b/src/util.js index 9f42afb13..c1ff10069 100644 --- a/src/util.js +++ b/src/util.js @@ -627,6 +627,25 @@ class Cache { } } +/** + * Removes color formatting from a text string. + * @param {string} text Text with color formatting + * @returns {string} Text without color formatting + */ +function removeColorFormatting(text) { + // ANSI escape codes for colors are usually in the format \x1b[m + return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, ''); +} + +/** + * Gets a separator string repeated n times. + * @param {number} n Number of times to repeat the separator + * @returns {string} Separator string + */ +function getSeparator(n) { + return '='.repeat(n); +} + module.exports = { getConfig, getConfigValue, @@ -654,4 +673,6 @@ module.exports = { trimV1, Cache, makeHttp2Request, + removeColorFormatting, + getSeparator, };