support for Ipv6 (#2593) * started adding v6 support * added error checking and change messages to the user * fixed lsp caused issue * fixed formatting error * added error handling to https * fixed formatting errors * brought server starting into different func and added enable v6 and v4 * added error checking for disabling both v6 and v4. added option to prefer v6 for dns. added that stuff to the default config * fixed dumb bug * changed to settings named disable ipvx * fixed failed ips still showing as listening * fixed error handling * changed ip protocol config layout * small const name changes * fixed no error if only available protocol fails, and changed wording of some errors * fixed error handling saying 'non-fatal error' for protocol fail even when it's the only one enabled * moved more logic to listen error handler * fixed eslint issues * added more info on when to prefer ipv6 for dns * in conf changed one 'ipv6' to 'IPv6' for consistency * changed error message and redid how starting the server works * removed unneeded log * removed unneeded log * removed unneeded comments * fixed errors * fixed errors * fixed errors * changed the wording of ip related error messages * removed empty lines * changed to .finally(startServer); * removed some whitespace * disabled ipv6 by default ╯︿╰ and changed some message wording * added auto mode for autorun hostname and changed formatting for listening log and added goto message with autorun url * added autorun port override * removed debug log * changed formatting * added cli args to ipv6 and autorun stuff * moved cli args around * changed formatting * changed colors for ip * added avoidLocalhost cli arg * changed formatting * changed to not print protocol on listening * added config option for avoid localhost and changed formatting of messages * fixed avoid localhost config option * Fix ipv4 color --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Signed| @@ -4,8 +4,18 @@ dataRoot: ./data | |||
| 4 | # -- SERVER CONFIGURATION -- | 4 | # -- SERVER CONFIGURATION -- |
| 5 | # Listen for incoming connections | 5 | # Listen for incoming connections |
| 6 | listen: false | 6 | listen: false |
| 7 | # Enables IPv6 and/or IPv4 | ||
| 8 | protocol: | ||
| 9 | ipv4: true | ||
| 10 | ipv6: false | ||
| 11 | # Prefers IPv6 for dns, you should probably enable this on ISPs that don't have issues with IPv6 | ||
| 12 | dnsPreferIPv6: false | ||
| 13 | # the hostname that autorun opens probably best left on auto. use options like 'localhost', 'st.example.com' | ||
| 14 | autorunHostname: "auto" | ||
| 7 | # Server port | 15 | # Server port |
| 8 | port: 8000 | 16 | port: 8000 |
| 17 | # 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 | ||
| 18 | autorunPortOverride: -1 | ||
| 9 | # -- SECURITY CONFIGURATION -- | 19 | # -- SECURITY CONFIGURATION -- |
| 10 | # Toggle whitelist mode | 20 | # Toggle whitelist mode |
| 11 | whitelistMode: true | 21 | whitelistMode: true |
| @@ -13,6 +23,7 @@ whitelistMode: true | |||
| 13 | enableForwardedWhitelist: true | 23 | enableForwardedWhitelist: true |
| 14 | # Whitelist of allowed IP addresses | 24 | # Whitelist of allowed IP addresses |
| 15 | whitelist: | 25 | whitelist: |
| 26 | - ::1 | ||
| 16 | - 127.0.0.1 | 27 | - 127.0.0.1 |
| 17 | # Toggle basic authentication for endpoints | 28 | # Toggle basic authentication for endpoints |
| 18 | basicAuthMode: false | 29 | basicAuthMode: false |
| @@ -35,6 +46,9 @@ securityOverride: false | |||
| 35 | # -- ADVANCED CONFIGURATION -- | 46 | # -- ADVANCED CONFIGURATION -- |
| 36 | # Open the browser automatically | 47 | # Open the browser automatically |
| 37 | autorun: true | 48 | autorun: true |
| 49 | # Avoids using 'localhost' for autorun in auto mode. | ||
| 50 | # use if you don't have 'localhost' in your hosts file | ||
| 51 | avoidLocalhost: false | ||
| 38 | # Disable thumbnail generation | 52 | # Disable thumbnail generation |
| 39 | disableThumbnails: false | 53 | disableThumbnails: false |
| 40 | # Thumbnail quality (0-100) | 54 | # Thumbnail quality (0-100) |
| @@ -58,7 +58,7 @@ | |||
| 58 | }, | 58 | }, |
| 59 | "devDependencies": { | 59 | "devDependencies": { |
| 60 | "@types/jquery": "^3.5.29", | 60 | "@types/jquery": "^3.5.29", |
| 61 | "eslint": "^8.55.0", | 61 | "eslint": "^8.57.0", |
| 62 | "jquery": "^3.6.4" | 62 | "jquery": "^3.6.4" |
| 63 | }, | 63 | }, |
| 64 | "engines": { | 64 | "engines": { |
| @@ -166,9 +166,9 @@ | |||
| 166 | "license": "MIT" | 166 | "license": "MIT" |
| 167 | }, | 167 | }, |
| 168 | "node_modules/@eslint/js": { | 168 | "node_modules/@eslint/js": { |
| 169 | "version": "8.55.0", | 169 | "version": "8.57.0", |
| 170 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", | 170 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", |
| 171 | "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", | 171 | "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", |
| 172 | "dev": true, | 172 | "dev": true, |
| 173 | "license": "MIT", | 173 | "license": "MIT", |
| 174 | "engines": { | 174 | "engines": { |
| @@ -185,14 +185,15 @@ | |||
| 185 | } | 185 | } |
| 186 | }, | 186 | }, |
| 187 | "node_modules/@humanwhocodes/config-array": { | 187 | "node_modules/@humanwhocodes/config-array": { |
| 188 | "version": "0.11.13", | 188 | "version": "0.11.14", |
| 189 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", | 189 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", |
| 190 | "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", | 190 | "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", |
| 191 | "deprecated": "Use @eslint/config-array instead", | ||
| 191 | "dev": true, | 192 | "dev": true, |
| 192 | "license": "Apache-2.0", | 193 | "license": "Apache-2.0", |
| 193 | "dependencies": { | 194 | "dependencies": { |
| 194 | "@humanwhocodes/object-schema": "^2.0.1", | 195 | "@humanwhocodes/object-schema": "^2.0.2", |
| 195 | "debug": "^4.1.1", | 196 | "debug": "^4.3.1", |
| 196 | "minimatch": "^3.0.5" | 197 | "minimatch": "^3.0.5" |
| 197 | }, | 198 | }, |
| 198 | "engines": { | 199 | "engines": { |
| @@ -200,9 +201,9 @@ | |||
| 200 | } | 201 | } |
| 201 | }, | 202 | }, |
| 202 | "node_modules/@humanwhocodes/config-array/node_modules/debug": { | 203 | "node_modules/@humanwhocodes/config-array/node_modules/debug": { |
| 203 | "version": "4.3.4", | 204 | "version": "4.3.6", |
| 204 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | 205 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", |
| 205 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", | 206 | "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", |
| 206 | "dev": true, | 207 | "dev": true, |
| 207 | "license": "MIT", | 208 | "license": "MIT", |
| 208 | "dependencies": { | 209 | "dependencies": { |
| @@ -239,9 +240,10 @@ | |||
| 239 | } | 240 | } |
| 240 | }, | 241 | }, |
| 241 | "node_modules/@humanwhocodes/object-schema": { | 242 | "node_modules/@humanwhocodes/object-schema": { |
| 242 | "version": "2.0.1", | 243 | "version": "2.0.3", |
| 243 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", | 244 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", |
| 244 | "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", | 245 | "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", |
| 246 | "deprecated": "Use @eslint/object-schema instead", | ||
| 245 | "dev": true, | 247 | "dev": true, |
| 246 | "license": "BSD-3-Clause" | 248 | "license": "BSD-3-Clause" |
| 247 | }, | 249 | }, |
| @@ -2455,17 +2457,17 @@ | |||
| 2455 | } | 2457 | } |
| 2456 | }, | 2458 | }, |
| 2457 | "node_modules/eslint": { | 2459 | "node_modules/eslint": { |
| 2458 | "version": "8.55.0", | 2460 | "version": "8.57.0", |
| 2459 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", | 2461 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", |
| 2460 | "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", | 2462 | "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", |
| 2461 | "dev": true, | 2463 | "dev": true, |
| 2462 | "license": "MIT", | 2464 | "license": "MIT", |
| 2463 | "dependencies": { | 2465 | "dependencies": { |
| 2464 | "@eslint-community/eslint-utils": "^4.2.0", | 2466 | "@eslint-community/eslint-utils": "^4.2.0", |
| 2465 | "@eslint-community/regexpp": "^4.6.1", | 2467 | "@eslint-community/regexpp": "^4.6.1", |
| 2466 | "@eslint/eslintrc": "^2.1.4", | 2468 | "@eslint/eslintrc": "^2.1.4", |
| 2467 | "@eslint/js": "8.55.0", | 2469 | "@eslint/js": "8.57.0", |
| 2468 | "@humanwhocodes/config-array": "^0.11.13", | 2470 | "@humanwhocodes/config-array": "^0.11.14", |
| 2469 | "@humanwhocodes/module-importer": "^1.0.1", | 2471 | "@humanwhocodes/module-importer": "^1.0.1", |
| 2470 | "@nodelib/fs.walk": "^1.2.8", | 2472 | "@nodelib/fs.walk": "^1.2.8", |
| 2471 | "@ungap/structured-clone": "^1.2.0", | 2473 | "@ungap/structured-clone": "^1.2.0", |
| @@ -90,7 +90,7 @@ | |||
| 90 | "main": "server.js", | 90 | "main": "server.js", |
| 91 | "devDependencies": { | 91 | "devDependencies": { |
| 92 | "@types/jquery": "^3.5.29", | 92 | "@types/jquery": "^3.5.29", |
| 93 | "eslint": "^8.55.0", | 93 | "eslint": "^8.57.0", |
| 94 | "jquery": "^3.6.4" | 94 | "jquery": "^3.6.4" |
| 95 | } | 95 | } |
| 96 | } | 96 | } |
| @@ -54,8 +54,9 @@ if (process.versions && process.versions.node && process.versions.node.match(/20 | |||
| 54 | if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false); | 54 | if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | // Set default DNS resolution order to IPv4 first | 57 | |
| 58 | dns.setDefaultResultOrder('ipv4first'); | 58 | |
| 59 | |||
| 59 | 60 | ||
| 60 | const DEFAULT_PORT = 8000; | 61 | const DEFAULT_PORT = 8000; |
| 61 | const DEFAULT_AUTORUN = false; | 62 | const DEFAULT_AUTORUN = false; |
| @@ -66,16 +67,46 @@ const DEFAULT_ACCOUNTS = false; | |||
| 66 | const DEFAULT_CSRF_DISABLED = false; | 67 | const DEFAULT_CSRF_DISABLED = false; |
| 67 | const DEFAULT_BASIC_AUTH = false; | 68 | const DEFAULT_BASIC_AUTH = false; |
| 68 | 69 | ||
| 70 | const DEFAULT_ENABLE_IPV6 = false; | ||
| 71 | const DEFAULT_ENABLE_IPV4 = true; | ||
| 72 | |||
| 73 | const DEFAULT_PREFER_IPV6 = false; | ||
| 74 | |||
| 75 | const DEFAULT_AVOID_LOCALHOST = false; | ||
| 76 | |||
| 77 | const DEFAULT_AUTORUN_HOSTNAME = 'auto'; | ||
| 78 | const DEFAULT_AUTORUN_PORT = -1; | ||
| 79 | |||
| 69 | const cliArguments = yargs(hideBin(process.argv)) | 80 | const cliArguments = yargs(hideBin(process.argv)) |
| 70 | .usage('Usage: <your-start-script> <command> [options]') | 81 | .usage('Usage: <your-start-script> <command> [options]') |
| 71 | .option('port', { | 82 | .option('enableIPv6', { |
| 83 | type: 'boolean', | ||
| 84 | default: null, | ||
| 85 | describe: `Enables IPv6.\n[config default: ${DEFAULT_ENABLE_IPV6}]`, | ||
| 86 | }).option('enableIPv4', { | ||
| 87 | type: 'boolean', | ||
| 88 | default: null, | ||
| 89 | describe: `Enables IPv4.\n[config default: ${DEFAULT_ENABLE_IPV4}]`, | ||
| 90 | }).option('port', { | ||
| 72 | type: 'number', | 91 | type: 'number', |
| 73 | default: null, | 92 | default: null, |
| 74 | describe: `Sets the port under which SillyTavern will run.\nIf not provided falls back to yaml config 'port'.\n[config default: ${DEFAULT_PORT}]`, | 93 | describe: `Sets the port under which SillyTavern will run.\nIf not provided falls back to yaml config 'port'.\n[config default: ${DEFAULT_PORT}]`, |
| 94 | }).option('dnsPreferIPv6', { | ||
| 95 | type: 'boolean', | ||
| 96 | default: null, | ||
| 97 | describe: `Prefers IPv6 for dns\nyou should probably have the enabled if you're on an IPv6 only network\nIf not provided falls back to yaml config 'preferIPv6'.\n[config default: ${DEFAULT_PREFER_IPV6}]`, | ||
| 75 | }).option('autorun', { | 98 | }).option('autorun', { |
| 76 | type: 'boolean', | 99 | type: 'boolean', |
| 77 | default: null, | 100 | default: null, |
| 78 | describe: `Automatically launch SillyTavern in the browser.\nAutorun is automatically disabled if --ssl is set to true.\nIf not provided falls back to yaml config 'autorun'.\n[config default: ${DEFAULT_AUTORUN}]`, | 101 | describe: `Automatically launch SillyTavern in the browser.\nAutorun is automatically disabled if --ssl is set to true.\nIf not provided falls back to yaml config 'autorun'.\n[config default: ${DEFAULT_AUTORUN}]`, |
| 102 | }).option('autorunHostname', { | ||
| 103 | type: 'string', | ||
| 104 | default: null, | ||
| 105 | describe: 'the autorun hostname, probably best left on \'auto\'.\nuse values like \'localhost\', \'st.example.com\'', | ||
| 106 | }).option('autorunPortOverride', { | ||
| 107 | type: 'string', | ||
| 108 | default: null, | ||
| 109 | describe: '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', | ||
| 79 | }).option('listen', { | 110 | }).option('listen', { |
| 80 | type: 'boolean', | 111 | type: 'boolean', |
| 81 | default: null, | 112 | default: null, |
| @@ -108,6 +139,10 @@ const cliArguments = yargs(hideBin(process.argv)) | |||
| 108 | type: 'string', | 139 | type: 'string', |
| 109 | default: null, | 140 | default: null, |
| 110 | describe: 'Root directory for data storage', | 141 | describe: 'Root directory for data storage', |
| 142 | }).option('avoidLocalhost', { | ||
| 143 | type: 'boolean', | ||
| 144 | default: null, | ||
| 145 | describe: 'Avoids using \'localhost\' for autorun in auto mode.\nuse if you don\'t have \'localhost\' in your hosts file', | ||
| 111 | }).option('basicAuthMode', { | 146 | }).option('basicAuthMode', { |
| 112 | type: 'boolean', | 147 | type: 'boolean', |
| 113 | default: null, | 148 | default: null, |
| @@ -138,6 +173,31 @@ const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS); | |||
| 138 | 173 | ||
| 139 | const uploadsPath = path.join(dataRoot, require('./src/constants').UPLOADS_DIRECTORY); | 174 | const uploadsPath = path.join(dataRoot, require('./src/constants').UPLOADS_DIRECTORY); |
| 140 | 175 | ||
| 176 | const enableIPv6 = cliArguments.enableIPv6 ?? getConfigValue('protocol.ipv6', DEFAULT_ENABLE_IPV6); | ||
| 177 | const enableIPv4 = cliArguments.enableIPv4 ?? getConfigValue('protocol.ipv4', DEFAULT_ENABLE_IPV4); | ||
| 178 | |||
| 179 | const autorunHostname = cliArguments.autorunHostname ?? getConfigValue('autorunHostname', DEFAULT_AUTORUN_HOSTNAME); | ||
| 180 | const autorunPortOverride = cliArguments.autorunPortOverride ?? getConfigValue('autorunPortOverride', DEFAULT_AUTORUN_PORT); | ||
| 181 | |||
| 182 | const dnsPreferIPv6 = cliArguments.dnsPreferIPv6 ?? getConfigValue('dnsPreferIPv6', DEFAULT_PREFER_IPV6); | ||
| 183 | |||
| 184 | const avoidLocalhost = cliArguments.avoidLocalhost ?? getConfigValue('avoidLocalhost', DEFAULT_AVOID_LOCALHOST); | ||
| 185 | |||
| 186 | if (dnsPreferIPv6) { | ||
| 187 | // Set default DNS resolution order to IPv6 first | ||
| 188 | dns.setDefaultResultOrder('ipv6first'); | ||
| 189 | console.log('Preferring IPv6 for DNS resolution'); | ||
| 190 | } else { | ||
| 191 | // Set default DNS resolution order to IPv4 first | ||
| 192 | dns.setDefaultResultOrder('ipv4first'); | ||
| 193 | console.log('Preferring IPv4 for DNS resolution'); | ||
| 194 | } | ||
| 195 | |||
| 196 | if (!enableIPv6 && !enableIPv4) { | ||
| 197 | console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.'); | ||
| 198 | process.exit(1); | ||
| 199 | } | ||
| 200 | |||
| 141 | // CORS Settings // | 201 | // CORS Settings // |
| 142 | const CORS = cors({ | 202 | const CORS = cors({ |
| 143 | origin: 'null', | 203 | origin: 'null', |
| @@ -546,18 +606,19 @@ app.use('/api/speech', require('./src/endpoints/speech').router); | |||
| 546 | // Azure TTS | 606 | // Azure TTS |
| 547 | app.use('/api/azure', require('./src/endpoints/azure').router); | 607 | app.use('/api/azure', require('./src/endpoints/azure').router); |
| 548 | 608 | ||
| 549 | const tavernUrl = new URL( | 609 | const tavernUrlV6 = new URL( |
| 550 | (cliArguments.ssl ? 'https://' : 'http://') + | 610 | (cliArguments.ssl ? 'https://' : 'http://') + |
| 551 | (listen ? '0.0.0.0' : '127.0.0.1') + | 611 | (listen ? '[::]' : '[::1]') + |
| 552 | (':' + server_port), | 612 | (':' + server_port), |
| 553 | ); | 613 | ); |
| 554 | 614 | ||
| 555 | const autorunUrl = new URL( | 615 | const tavernUrl = new URL( |
| 556 | (cliArguments.ssl ? 'https://' : 'http://') + | 616 | (cliArguments.ssl ? 'https://' : 'http://') + |
| 557 | ('127.0.0.1') + | 617 | (listen ? '0.0.0.0' : '127.0.0.1') + |
| 558 | (':' + server_port), | 618 | (':' + server_port), |
| 559 | ); | 619 | ); |
| 560 | 620 | ||
| 621 | |||
| 561 | /** | 622 | /** |
| 562 | * Tasks that need to be run before the server starts listening. | 623 | * Tasks that need to be run before the server starts listening. |
| 563 | */ | 624 | */ |
| @@ -606,20 +667,83 @@ const preSetupTasks = async function () { | |||
| 606 | }); | 667 | }); |
| 607 | }; | 668 | }; |
| 608 | 669 | ||
| 670 | function removeColorFormatting(text) { | ||
| 671 | // ANSI escape codes for colors are usually in the format \x1b[<codes>m | ||
| 672 | return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, ''); | ||
| 673 | } | ||
| 674 | |||
| 675 | function getSeparator(n) { | ||
| 676 | return '='.repeat(n); | ||
| 677 | } | ||
| 678 | |||
| 679 | |||
| 680 | |||
| 681 | function getAutorunHostname() { | ||
| 682 | |||
| 683 | if (autorunHostname === 'auto') { | ||
| 684 | if (enableIPv6 && enableIPv4) { | ||
| 685 | if (avoidLocalhost) return '[::1]'; | ||
| 686 | return 'localhost'; | ||
| 687 | } | ||
| 688 | |||
| 689 | if (enableIPv6) { | ||
| 690 | return '[::1]'; | ||
| 691 | } | ||
| 692 | |||
| 693 | if (enableIPv4) { | ||
| 694 | return '127.0.0.1'; | ||
| 695 | } | ||
| 696 | } | ||
| 697 | |||
| 698 | return autorunHostname; | ||
| 699 | } | ||
| 700 | |||
| 701 | |||
| 609 | /** | 702 | /** |
| 610 | * Tasks that need to be run after the server starts listening. | 703 | * Tasks that need to be run after the server starts listening. |
| 611 | */ | 704 | */ |
| 612 | const postSetupTasks = async function () { | 705 | const postSetupTasks = async function (v6Failed, v4Failed) { |
| 706 | |||
| 707 | |||
| 708 | const autorunUrl = new URL( | ||
| 709 | (cliArguments.ssl ? 'https://' : 'http://') + | ||
| 710 | (getAutorunHostname()) + | ||
| 711 | (':') + | ||
| 712 | ((autorunPortOverride >= 0) ? autorunPortOverride : server_port), | ||
| 713 | ); | ||
| 714 | |||
| 715 | |||
| 613 | console.log('Launching...'); | 716 | console.log('Launching...'); |
| 614 | 717 | ||
| 615 | if (autorun) open(autorunUrl.toString()); | 718 | if (autorun) open(autorunUrl.toString()); |
| 616 | 719 | ||
| 617 | setWindowTitle('SillyTavern WebServer'); | 720 | setWindowTitle('SillyTavern WebServer'); |
| 618 | 721 | ||
| 619 | console.log(color.green('SillyTavern is listening on: ' + tavernUrl)); | 722 | |
| 723 | let ipv6Color = color.green; | ||
| 724 | let ipv4Color = color.green; | ||
| 725 | let autorunColor = color.blue; | ||
| 726 | |||
| 727 | let logListen = 'SillyTavern is listening on'; | ||
| 728 | |||
| 729 | if (enableIPv6 && !v6Failed) { | ||
| 730 | logListen += ipv6Color(' IPv6: ' + tavernUrlV6.host); | ||
| 731 | } | ||
| 732 | |||
| 733 | if (enableIPv4 && !v4Failed) { | ||
| 734 | logListen += ipv4Color(' IPv4: ' + tavernUrl.host); | ||
| 735 | } | ||
| 736 | |||
| 737 | let goToLog = 'Go to: ' + autorunColor(autorunUrl) + ' to open SillyTavern'; | ||
| 738 | let plainGoToLog = removeColorFormatting(goToLog); | ||
| 739 | |||
| 740 | console.log(logListen); | ||
| 741 | console.log('\n' + getSeparator(plainGoToLog.length) + '\n'); | ||
| 742 | console.log(goToLog); | ||
| 743 | console.log('\n' + getSeparator(plainGoToLog.length) + '\n'); | ||
| 620 | 744 | ||
| 621 | if (listen) { | 745 | if (listen) { |
| 622 | console.log('\n0.0.0.0 means SillyTavern is listening on all network interfaces (Wi-Fi, LAN, localhost). If you want to limit it only to internal localhost (127.0.0.1), change the setting in config.yaml to "listen: false". Check "access.log" file in the SillyTavern directory if you want to inspect incoming connections.\n'); | 746 | console.log('[::] or 0.0.0.0 means SillyTavern is listening on all network interfaces (Wi-Fi, LAN, localhost). If you want to limit it only to internal localhost ([::1] or 127.0.0.1), change the setting in config.yaml to "listen: false". Check "access.log" file in the SillyTavern directory if you want to inspect incoming connections.\n'); |
| 623 | } | 747 | } |
| 624 | 748 | ||
| 625 | if (basicAuthMode) { | 749 | if (basicAuthMode) { |
| @@ -674,6 +798,102 @@ function logSecurityAlert(message) { | |||
| 674 | process.exit(1); | 798 | process.exit(1); |
| 675 | } | 799 | } |
| 676 | 800 | ||
| 801 | |||
| 802 | |||
| 803 | function handleServerListenFail(v6Failed, v4Failed) { | ||
| 804 | if (v6Failed && !enableIPv4) { | ||
| 805 | console.error('fatal error: Failed to start server on IPv6 and IPv4 disabled'); | ||
| 806 | process.exit(1); | ||
| 807 | } | ||
| 808 | |||
| 809 | if (v4Failed && !enableIPv6) { | ||
| 810 | console.error('fatal error: Failed to start server on IPv4 and IPv6 disabled'); | ||
| 811 | process.exit(1); | ||
| 812 | } | ||
| 813 | |||
| 814 | if (v6Failed && v4Failed) { | ||
| 815 | console.error('fatal error: Failed to start server on both IPv6 and IPv4'); | ||
| 816 | process.exit(1); | ||
| 817 | } | ||
| 818 | } | ||
| 819 | |||
| 820 | |||
| 821 | function createHttpsServer(url) { | ||
| 822 | return new Promise((resolve, reject) => { | ||
| 823 | const server = https.createServer( | ||
| 824 | { | ||
| 825 | cert: fs.readFileSync(cliArguments.certPath), | ||
| 826 | key: fs.readFileSync(cliArguments.keyPath), | ||
| 827 | }, app); | ||
| 828 | server.on('error', reject); | ||
| 829 | server.on('listening', resolve); | ||
| 830 | server.listen(url.port, url.hostname); | ||
| 831 | }); | ||
| 832 | } | ||
| 833 | |||
| 834 | function createHttpServer(url) { | ||
| 835 | return new Promise((resolve, reject) => { | ||
| 836 | const server = http.createServer(app); | ||
| 837 | server.on('error', reject); | ||
| 838 | server.on('listening', resolve); | ||
| 839 | server.listen(url.port, url.hostname); | ||
| 840 | }); | ||
| 841 | } | ||
| 842 | |||
| 843 | |||
| 844 | |||
| 845 | |||
| 846 | async function startHTTPorHTTPS() { | ||
| 847 | let v6Failed = false; | ||
| 848 | let v4Failed = false; | ||
| 849 | |||
| 850 | let createFunc = createHttpServer; | ||
| 851 | if (cliArguments.ssl) { | ||
| 852 | createFunc = createHttpsServer; | ||
| 853 | } | ||
| 854 | |||
| 855 | if (enableIPv6) { | ||
| 856 | try { | ||
| 857 | await createFunc(tavernUrlV6); | ||
| 858 | } catch(error) { | ||
| 859 | if (enableIPv4) { | ||
| 860 | console.error('non-fatal error: failed to start server on IPv6', error); | ||
| 861 | } | ||
| 862 | |||
| 863 | v6Failed = true; | ||
| 864 | } | ||
| 865 | } | ||
| 866 | |||
| 867 | if (enableIPv4) { | ||
| 868 | try { | ||
| 869 | await createFunc(tavernUrl); | ||
| 870 | } catch(error) { | ||
| 871 | if (enableIPv6) { | ||
| 872 | console.error('non-fatal error: failed to start server on IPv4', error); | ||
| 873 | } | ||
| 874 | |||
| 875 | v4Failed = true; | ||
| 876 | } | ||
| 877 | } | ||
| 878 | return [v6Failed, v4Failed]; | ||
| 879 | } | ||
| 880 | |||
| 881 | |||
| 882 | |||
| 883 | |||
| 884 | async function startServer() { | ||
| 885 | let v6Failed = false; | ||
| 886 | let v4Failed = false; | ||
| 887 | |||
| 888 | |||
| 889 | [v6Failed, v4Failed] = await startHTTPorHTTPS(); | ||
| 890 | |||
| 891 | handleServerListenFail(v6Failed, v4Failed); | ||
| 892 | postSetupTasks(v6Failed, v4Failed); | ||
| 893 | } | ||
| 894 | |||
| 895 | |||
| 896 | |||
| 677 | async function verifySecuritySettings() { | 897 | async function verifySecuritySettings() { |
| 678 | // Skip all security checks as listen is set to false | 898 | // Skip all security checks as listen is set to false |
| 679 | if (!listen) { | 899 | if (!listen) { |
| @@ -707,23 +927,4 @@ userModule.initUserStorage(dataRoot) | |||
| 707 | .then(userModule.migrateUserData) | 927 | .then(userModule.migrateUserData) |
| 708 | .then(verifySecuritySettings) | 928 | .then(verifySecuritySettings) |
| 709 | .then(preSetupTasks) | 929 | .then(preSetupTasks) |
| 710 | .finally(() => { | 930 | .finally(startServer); |
| 711 | if (cliArguments.ssl) { | ||
| 712 | https.createServer( | ||
| 713 | { | ||
| 714 | cert: fs.readFileSync(cliArguments.certPath), | ||
| 715 | key: fs.readFileSync(cliArguments.keyPath), | ||
| 716 | }, app) | ||
| 717 | .listen( | ||
| 718 | Number(tavernUrl.port) || 443, | ||
| 719 | tavernUrl.hostname, | ||
| 720 | postSetupTasks, | ||
| 721 | ); | ||
| 722 | } else { | ||
| 723 | http.createServer(app).listen( | ||
| 724 | Number(tavernUrl.port) || 80, | ||
| 725 | tavernUrl.hostname, | ||
| 726 | postSetupTasks, | ||
| 727 | ); | ||
| 728 | } | ||
| 729 | }); | ||