Support specifing `config.yaml` in cli

26a520af1075f053982b8dab718e5a4b4ef09d1c

wrvsrx <wrvsrx@outlook.com>

Signed
3 files changed, +71 -42Showing whitespace changes
server.js+41 -38
@@ -20,10 +20,28 @@ import open from 'open';
2020
2121// local library imports
2222import './src/fetch-patch.js';
23-import { serverEvents, EVENT_NAMES } from './src/server-events.js';
2423import { CommandLineParser } from './src/command-line.js';
2524import { loadPluginsserverDirectory } from './src/pluginserver-loaderdirectory.js';
26-import {
25+
26+console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment. Server directory: ${serverDirectory}`);
27+
28+// Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0.
29+// https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
30+// Safe to remove once support for Node v20 is dropped.
31+if (process.versions && process.versions.node && process.versions.node.match(/20\.[0-2]\.0/)) {
32+ // @ts-ignore
33+ if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
34+}
35+
36+// config.yaml will be set when parsing command line arguments
37+const cliArgs = new CommandLineParser().parse(process.argv);
38+globalThis.DATA_ROOT = cliArgs.dataRoot;
39+globalThis.COMMAND_LINE_ARGS = cliArgs;
40+process.chdir(serverDirectory);
41+
42+const { serverEvents, EVENT_NAMES } = await import('./src/server-events.js');
43+const { loadPlugins } = await import('./src/plugin-loader.js');
44+const {
2745 initUserStorage,
2846 getCookieSecret,
2947 getCookieSessionName,
@@ -38,17 +56,17 @@ import {
3856 getSessionCookieAge,
3957 verifySecuritySettings,
4058 loginPageMiddleware,
4159} from= await import('./src/users.js');
4260
4361importconst { default: getWebpackServeMiddleware from} = await import('./src/middleware/webpack-serve.js');
4462importconst { default: basicAuthMiddleware from} = await import('./src/middleware/basicAuth.js');
4563importconst { default: getWhitelistMiddleware from} = await import('./src/middleware/whitelist.js');
4664importconst { default: accessLoggerMiddleware, { getAccessLogPath, migrateAccessLog } from= await import('./src/middleware/accessLogWriter.js');
4765importconst { default: multerMonkeyPatch from} = await import('./src/middleware/multerMonkeyPatch.js');
4866importconst { default: initRequestProxy from} = await import('./src/request-proxy.js');
4967importconst { default: getCacheBusterMiddleware from} = await import('./src/middleware/cacheBuster.js');
5068importconst { default: corsProxyMiddleware from} = await import('./src/middleware/corsProxy.js');
5169importconst {
5270 getVersion,
5371 color,
5472 removeColorFormatting,
@@ -56,38 +74,23 @@ import {
5674 safeReadFileSync,
5775 setupLogLevel,
5876 setWindowTitle,
5977} from= await import('./src/util.js');
6078importconst { UPLOADS_DIRECTORY } from= await import('./src/constants.js');
6179importconst { ensureThumbnailCache } from= await import('./src/endpoints/thumbnails.js');
62-import { serverDirectory } from './src/server-directory.js';
6380
6481// Routers
6582importconst { router as: usersPublicRouter } from= await import('./src/endpoints/users-public.js');
6683importconst { init as: statsInit, onExit as: statsOnExit } from= await import('./src/endpoints/stats.js');
6784importconst { checkForNewContent } from= await import('./src/endpoints/content-manager.js');
6885importconst { init as: settingsInit } from= await import('./src/endpoints/settings.js');
6986importconst { redirectDeprecatedEndpoints, ServerStartup, setupPrivateEndpoints } from= await import('./src/server-startup.js');
7087importconst { diskCache } from= await import('./src/endpoints/characters.js');
7188
7289// Unrestrict console logs display limit
7390util.inspect.defaultOptions.maxArrayLength = null;
7491util.inspect.defaultOptions.maxStringLength = null;
7592util.inspect.defaultOptions.depth = 4;
7693
77-console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment. Server directory: ${serverDirectory}`);
78-
79-// Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0.
80-// https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
81-// Safe to remove once support for Node v20 is dropped.
82-if (process.versions && process.versions.node && process.versions.node.match(/20\.[0-2]\.0/)) {
83- // @ts-ignore
84- if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
85-}
86-
87-const cliArgs = new CommandLineParser().parse(process.argv);
88-globalThis.DATA_ROOT = cliArgs.dataRoot;
89-globalThis.COMMAND_LINE_ARGS = cliArgs;
90-
9194if (!cliArgs.enableIPv6 && !cliArgs.enableIPv4) {
9295 console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.');
9396 process.exit(1);
src/command-line.js+11 -1
@@ -1,10 +1,11 @@
11import yargs from 'yargs/yargs';
22import { hideBin } from 'yargs/helpers';
33import ipRegex from 'ip-regex';
44import { canResolve, color, getConfigValue, setConfigFilePath, stringToBool } from './util.js';
55
66/**
77 * @typedef {object} CommandLineArguments Parsed command line arguments
8+ * @property {string} configPath Path to the config file
89 * @property {string} dataRoot Data root directory
910 * @property {number} port Port number
1011 * @property {boolean} listen If SillyTavern is listening on all network interfaces
@@ -40,6 +41,7 @@ export class CommandLineParser {
4041 constructor() {
4142 /** @type {CommandLineArguments} */
4243 this.default = Object.freeze({
44+ configPath: './config.yaml',
4345 dataRoot: './data',
4446 port: 8000,
4547 listen: false,
@@ -88,6 +90,11 @@ export class CommandLineParser {
8890 parse(args) {
8991 const cliArguments = yargs(hideBin(args))
9092 .usage('Usage: <your-start-script> [options]\nOptions that are not provided will be filled with config values.')
93+ .option('configPath', {
94+ type: 'string',
95+ default: null,
96+ describe: 'Path to the config file',
97+ })
9198 .option('enableIPv6', {
9299 type: 'string',
93100 default: null,
@@ -177,8 +184,11 @@ export class CommandLineParser {
177184 describe: 'Request proxy bypass list (space separated list of hosts)',
178185 }).parseSync();
179186
187+ const configPath = cliArguments.configPath ?? this.default.configPath;
188+ setConfigFilePath(configPath);
180189 /** @type {CommandLineArguments} */
181190 const result = {
191+ configPath: configPath,
182192 dataRoot: cliArguments.dataRoot ?? getConfigValue('dataRoot', this.default.dataRoot),
183193 port: cliArguments.port ?? getConfigValue('port', this.default.port, 'number'),
184194 listen: cliArguments.listen ?? getConfigValue('listen', this.default.listen, 'boolean'),
src/util.js+19 -3
@@ -23,6 +23,7 @@ import { serverDirectory } from './server-directory.js';
2323 * Parsed config object.
2424 */
2525let CACHED_CONFIG = null;
26+let CONFIG_FILE = null;
2627
2728/**
2829 * Converts a configuration key to an environment variable key.
@@ -33,22 +34,37 @@ let CACHED_CONFIG = null;
3334export const keyToEnv = (key) => 'SILLYTAVERN_' + String(key).toUpperCase().replace(/\./g, '_');
3435
3536/**
37+ * Set the config file path.
38+ * @param {string} configFilePath Path to the config file
39+ */
40+export function setConfigFilePath(configFilePath) {
41+ if (CONFIG_FILE !== null) {
42+ console.error(color.red('Config file path already set. Please restart the server to change the config file path.'));
43+ }
44+ CONFIG_FILE = path.resolve(configFilePath);
45+}
46+
47+/**
3648 * Returns the config object from the config.yaml file.
3749 * @returns {object} Config object
3850 */
3951export function getConfig() {
52+ if (CONFIG_FILE === null) {
53+ console.trace();
54+ console.error(color.red('No config file path set. Please set the config file path using setConfigFilePath().'));
55+ process.exit(1);
56+ }
4057 if (CACHED_CONFIG) {
4158 return CACHED_CONFIG;
4259 }
43-
60+ if (!fs.existsSync(CONFIG_FILE)) {
44- if (!fs.existsSync('./config.yaml')) {
4561 console.error(color.red('No config file found. Please create a config.yaml file. The default config file can be found in the /default folder.'));
4662 console.error(color.red('The program will now exit.'));
4763 process.exit(1);
4864 }
4965
5066 try {
5167 const config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml')CONFIG_FILE, 'utf8'));
5268 CACHED_CONFIG = config;
5369 return config;
5470 } catch (error) {