mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
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>
This commit is contained in:
263
server.js
263
server.js
@ -54,8 +54,9 @@ if (process.versions && process.versions.node && process.versions.node.match(/20
|
||||
if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
|
||||
}
|
||||
|
||||
// Set default DNS resolution order to IPv4 first
|
||||
dns.setDefaultResultOrder('ipv4first');
|
||||
|
||||
|
||||
|
||||
|
||||
const DEFAULT_PORT = 8000;
|
||||
const DEFAULT_AUTORUN = false;
|
||||
@ -66,16 +67,46 @@ const DEFAULT_ACCOUNTS = false;
|
||||
const DEFAULT_CSRF_DISABLED = false;
|
||||
const DEFAULT_BASIC_AUTH = false;
|
||||
|
||||
const DEFAULT_ENABLE_IPV6 = false;
|
||||
const DEFAULT_ENABLE_IPV4 = true;
|
||||
|
||||
const DEFAULT_PREFER_IPV6 = false;
|
||||
|
||||
const DEFAULT_AVOID_LOCALHOST = false;
|
||||
|
||||
const DEFAULT_AUTORUN_HOSTNAME = 'auto';
|
||||
const DEFAULT_AUTORUN_PORT = -1;
|
||||
|
||||
const cliArguments = yargs(hideBin(process.argv))
|
||||
.usage('Usage: <your-start-script> <command> [options]')
|
||||
.option('port', {
|
||||
.option('enableIPv6', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
describe: `Enables IPv6.\n[config default: ${DEFAULT_ENABLE_IPV6}]`,
|
||||
}).option('enableIPv4', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
describe: `Enables IPv4.\n[config default: ${DEFAULT_ENABLE_IPV4}]`,
|
||||
}).option('port', {
|
||||
type: 'number',
|
||||
default: null,
|
||||
describe: `Sets the port under which SillyTavern will run.\nIf not provided falls back to yaml config 'port'.\n[config default: ${DEFAULT_PORT}]`,
|
||||
}).option('dnsPreferIPv6', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
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}]`,
|
||||
}).option('autorun', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
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}]`,
|
||||
}).option('autorunHostname', {
|
||||
type: 'string',
|
||||
default: null,
|
||||
describe: 'the autorun hostname, probably best left on \'auto\'.\nuse values like \'localhost\', \'st.example.com\'',
|
||||
}).option('autorunPortOverride', {
|
||||
type: 'string',
|
||||
default: null,
|
||||
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',
|
||||
}).option('listen', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
@ -108,6 +139,10 @@ const cliArguments = yargs(hideBin(process.argv))
|
||||
type: 'string',
|
||||
default: null,
|
||||
describe: 'Root directory for data storage',
|
||||
}).option('avoidLocalhost', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
describe: 'Avoids using \'localhost\' for autorun in auto mode.\nuse if you don\'t have \'localhost\' in your hosts file',
|
||||
}).option('basicAuthMode', {
|
||||
type: 'boolean',
|
||||
default: null,
|
||||
@ -138,6 +173,31 @@ const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS);
|
||||
|
||||
const uploadsPath = path.join(dataRoot, require('./src/constants').UPLOADS_DIRECTORY);
|
||||
|
||||
const enableIPv6 = cliArguments.enableIPv6 ?? getConfigValue('protocol.ipv6', DEFAULT_ENABLE_IPV6);
|
||||
const enableIPv4 = cliArguments.enableIPv4 ?? getConfigValue('protocol.ipv4', DEFAULT_ENABLE_IPV4);
|
||||
|
||||
const autorunHostname = cliArguments.autorunHostname ?? getConfigValue('autorunHostname', DEFAULT_AUTORUN_HOSTNAME);
|
||||
const autorunPortOverride = cliArguments.autorunPortOverride ?? getConfigValue('autorunPortOverride', DEFAULT_AUTORUN_PORT);
|
||||
|
||||
const dnsPreferIPv6 = cliArguments.dnsPreferIPv6 ?? getConfigValue('dnsPreferIPv6', DEFAULT_PREFER_IPV6);
|
||||
|
||||
const avoidLocalhost = cliArguments.avoidLocalhost ?? getConfigValue('avoidLocalhost', DEFAULT_AVOID_LOCALHOST);
|
||||
|
||||
if (dnsPreferIPv6) {
|
||||
// Set default DNS resolution order to IPv6 first
|
||||
dns.setDefaultResultOrder('ipv6first');
|
||||
console.log('Preferring IPv6 for DNS resolution');
|
||||
} else {
|
||||
// Set default DNS resolution order to IPv4 first
|
||||
dns.setDefaultResultOrder('ipv4first');
|
||||
console.log('Preferring IPv4 for DNS resolution');
|
||||
}
|
||||
|
||||
if (!enableIPv6 && !enableIPv4) {
|
||||
console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// CORS Settings //
|
||||
const CORS = cors({
|
||||
origin: 'null',
|
||||
@ -546,17 +606,18 @@ app.use('/api/speech', require('./src/endpoints/speech').router);
|
||||
// Azure TTS
|
||||
app.use('/api/azure', require('./src/endpoints/azure').router);
|
||||
|
||||
const tavernUrlV6 = new URL(
|
||||
(cliArguments.ssl ? 'https://' : 'http://') +
|
||||
(listen ? '[::]' : '[::1]') +
|
||||
(':' + server_port),
|
||||
);
|
||||
|
||||
const tavernUrl = new URL(
|
||||
(cliArguments.ssl ? 'https://' : 'http://') +
|
||||
(listen ? '0.0.0.0' : '127.0.0.1') +
|
||||
(':' + server_port),
|
||||
);
|
||||
|
||||
const autorunUrl = new URL(
|
||||
(cliArguments.ssl ? 'https://' : 'http://') +
|
||||
('127.0.0.1') +
|
||||
(':' + server_port),
|
||||
);
|
||||
|
||||
/**
|
||||
* Tasks that need to be run before the server starts listening.
|
||||
@ -606,20 +667,83 @@ const preSetupTasks = async function () {
|
||||
});
|
||||
};
|
||||
|
||||
function removeColorFormatting(text) {
|
||||
// ANSI escape codes for colors are usually in the format \x1b[<codes>m
|
||||
return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, '');
|
||||
}
|
||||
|
||||
function getSeparator(n) {
|
||||
return '='.repeat(n);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getAutorunHostname() {
|
||||
|
||||
if (autorunHostname === 'auto') {
|
||||
if (enableIPv6 && enableIPv4) {
|
||||
if (avoidLocalhost) return '[::1]';
|
||||
return 'localhost';
|
||||
}
|
||||
|
||||
if (enableIPv6) {
|
||||
return '[::1]';
|
||||
}
|
||||
|
||||
if (enableIPv4) {
|
||||
return '127.0.0.1';
|
||||
}
|
||||
}
|
||||
|
||||
return autorunHostname;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tasks that need to be run after the server starts listening.
|
||||
*/
|
||||
const postSetupTasks = async function () {
|
||||
const postSetupTasks = async function (v6Failed, v4Failed) {
|
||||
|
||||
|
||||
const autorunUrl = new URL(
|
||||
(cliArguments.ssl ? 'https://' : 'http://') +
|
||||
(getAutorunHostname()) +
|
||||
(':') +
|
||||
((autorunPortOverride >= 0) ? autorunPortOverride : server_port),
|
||||
);
|
||||
|
||||
|
||||
console.log('Launching...');
|
||||
|
||||
if (autorun) open(autorunUrl.toString());
|
||||
|
||||
setWindowTitle('SillyTavern WebServer');
|
||||
|
||||
console.log(color.green('SillyTavern is listening on: ' + tavernUrl));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (enableIPv4 && !v4Failed) {
|
||||
logListen += ipv4Color(' IPv4: ' + tavernUrl.host);
|
||||
}
|
||||
|
||||
let goToLog = 'Go to: ' + autorunColor(autorunUrl) + ' to open SillyTavern';
|
||||
let plainGoToLog = removeColorFormatting(goToLog);
|
||||
|
||||
console.log(logListen);
|
||||
console.log('\n' + getSeparator(plainGoToLog.length) + '\n');
|
||||
console.log(goToLog);
|
||||
console.log('\n' + getSeparator(plainGoToLog.length) + '\n');
|
||||
|
||||
if (listen) {
|
||||
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');
|
||||
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');
|
||||
}
|
||||
|
||||
if (basicAuthMode) {
|
||||
@ -674,6 +798,102 @@ function logSecurityAlert(message) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function handleServerListenFail(v6Failed, v4Failed) {
|
||||
if (v6Failed && !enableIPv4) {
|
||||
console.error('fatal error: Failed to start server on IPv6 and IPv4 disabled');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (v4Failed && !enableIPv6) {
|
||||
console.error('fatal error: Failed to start server on IPv4 and IPv6 disabled');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (v6Failed && v4Failed) {
|
||||
console.error('fatal error: Failed to start server on both IPv6 and IPv4');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createHttpsServer(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = https.createServer(
|
||||
{
|
||||
cert: fs.readFileSync(cliArguments.certPath),
|
||||
key: fs.readFileSync(cliArguments.keyPath),
|
||||
}, app);
|
||||
server.on('error', reject);
|
||||
server.on('listening', resolve);
|
||||
server.listen(url.port, url.hostname);
|
||||
});
|
||||
}
|
||||
|
||||
function createHttpServer(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = http.createServer(app);
|
||||
server.on('error', reject);
|
||||
server.on('listening', resolve);
|
||||
server.listen(url.port, url.hostname);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function startHTTPorHTTPS() {
|
||||
let v6Failed = false;
|
||||
let v4Failed = false;
|
||||
|
||||
let createFunc = createHttpServer;
|
||||
if (cliArguments.ssl) {
|
||||
createFunc = createHttpsServer;
|
||||
}
|
||||
|
||||
if (enableIPv6) {
|
||||
try {
|
||||
await createFunc(tavernUrlV6);
|
||||
} catch(error) {
|
||||
if (enableIPv4) {
|
||||
console.error('non-fatal error: failed to start server on IPv6', error);
|
||||
}
|
||||
|
||||
v6Failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableIPv4) {
|
||||
try {
|
||||
await createFunc(tavernUrl);
|
||||
} catch(error) {
|
||||
if (enableIPv6) {
|
||||
console.error('non-fatal error: failed to start server on IPv4', error);
|
||||
}
|
||||
|
||||
v4Failed = true;
|
||||
}
|
||||
}
|
||||
return [v6Failed, v4Failed];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function startServer() {
|
||||
let v6Failed = false;
|
||||
let v4Failed = false;
|
||||
|
||||
|
||||
[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) {
|
||||
@ -707,23 +927,4 @@ userModule.initUserStorage(dataRoot)
|
||||
.then(userModule.migrateUserData)
|
||||
.then(verifySecuritySettings)
|
||||
.then(preSetupTasks)
|
||||
.finally(() => {
|
||||
if (cliArguments.ssl) {
|
||||
https.createServer(
|
||||
{
|
||||
cert: fs.readFileSync(cliArguments.certPath),
|
||||
key: fs.readFileSync(cliArguments.keyPath),
|
||||
}, app)
|
||||
.listen(
|
||||
Number(tavernUrl.port) || 443,
|
||||
tavernUrl.hostname,
|
||||
postSetupTasks,
|
||||
);
|
||||
} else {
|
||||
http.createServer(app).listen(
|
||||
Number(tavernUrl.port) || 80,
|
||||
tavernUrl.hostname,
|
||||
postSetupTasks,
|
||||
);
|
||||
}
|
||||
});
|
||||
.finally(startServer);
|
||||
|
Reference in New Issue
Block a user