SillyTavern/server.js

589 lines
20 KiB
JavaScript
Raw Normal View History

2023-07-20 19:32:15 +02:00
#!/usr/bin/env node
2023-08-29 23:05:18 +02:00
// native node modules
2023-08-29 23:20:37 +02:00
const crypto = require('crypto');
2023-08-29 23:06:37 +02:00
const fs = require('fs');
2023-12-02 19:04:51 +01:00
const http = require('http');
2023-08-29 23:20:37 +02:00
const https = require('https');
2023-08-29 23:06:37 +02:00
const path = require('path');
const util = require('util');
2023-08-29 23:05:18 +02:00
2023-08-29 23:16:39 +02:00
// cli/fs related library imports
2023-08-29 23:26:59 +02:00
const open = require('open');
2023-08-29 23:23:53 +02:00
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
2023-08-29 23:16:39 +02:00
// express/server related library imports
const cors = require('cors');
const doubleCsrf = require('csrf-csrf').doubleCsrf;
2023-08-29 23:10:40 +02:00
const express = require('express');
const compression = require('compression');
2023-08-29 23:23:53 +02:00
const cookieParser = require('cookie-parser');
2023-12-02 19:04:51 +01:00
const multer = require('multer');
2023-08-29 23:23:53 +02:00
const responseTime = require('response-time');
2023-08-29 23:26:59 +02:00
// net related library imports
2023-12-02 19:04:51 +01:00
const net = require('net');
const dns = require('dns');
const fetch = require('node-fetch').default;
2023-08-29 23:10:40 +02:00
2023-09-10 03:12:14 +02:00
// Unrestrict console logs display limit
util.inspect.defaultOptions.maxArrayLength = null;
util.inspect.defaultOptions.maxStringLength = null;
util.inspect.defaultOptions.depth = 4;
2023-09-10 03:12:14 +02:00
2023-08-29 23:26:59 +02:00
// local library imports
2023-12-15 17:43:00 +01:00
const basicAuthMiddleware = require('./src/middleware/basicAuth');
const whitelistMiddleware = require('./src/middleware/whitelist');
2023-12-03 14:52:43 +01:00
const contentManager = require('./src/endpoints/content-manager');
const {
getVersion,
getConfigValue,
color,
forwardFetchResponse,
} = require('./src/util');
const { ensureThumbnailCache } = require('./src/endpoints/thumbnails');
const { loadTokenizers } = require('./src/endpoints/tokenizers');
2023-08-29 23:12:47 +02:00
2023-09-10 17:22:39 +02:00
// Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0.
// https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
// Safe to remove once support for Node v20 is dropped.
if (process.versions && process.versions.node && process.versions.node.match(/20\.[0-2]\.0/)) {
// @ts-ignore
if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
}
2023-07-20 19:32:15 +02:00
// Set default DNS resolution order to IPv4 first
dns.setDefaultResultOrder('ipv4first');
const DEFAULT_PORT = 8000;
const DEFAULT_AUTORUN = false;
const DEFAULT_LISTEN = false;
const DEFAULT_CORS_PROXY = false;
2023-07-20 19:32:15 +02:00
const cliArguments = yargs(hideBin(process.argv))
.usage('Usage: <your-start-script> <command> [options]')
.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('autorun', {
2023-11-18 01:09:42 +01:00
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('listen', {
type: 'boolean',
default: null,
describe: `SillyTavern is listening on all network interfaces (Wi-Fi, LAN, localhost). If false, will limit it only to internal localhost (127.0.0.1).\nIf not provided falls back to yaml config 'listen'.\n[config default: ${DEFAULT_LISTEN}]`,
2023-11-25 20:56:57 +01:00
}).option('corsProxy', {
type: 'boolean',
default: null,
describe: `Enables CORS proxy\nIf not provided falls back to yaml config 'enableCorsProxy'.\n[config default: ${DEFAULT_CORS_PROXY}]`,
2023-11-25 20:56:57 +01:00
}).option('disableCsrf', {
2023-08-06 15:42:15 +02:00
type: 'boolean',
default: false,
2023-12-02 21:06:57 +01:00
describe: 'Disables CSRF protection',
2023-08-06 15:42:15 +02:00
}).option('ssl', {
2023-07-20 19:32:15 +02:00
type: 'boolean',
default: false,
2023-12-02 21:06:57 +01:00
describe: 'Enables SSL',
2023-07-20 19:32:15 +02:00
}).option('certPath', {
type: 'string',
default: 'certs/cert.pem',
2023-12-02 21:06:57 +01:00
describe: 'Path to your certificate file.',
2023-07-20 19:32:15 +02:00
}).option('keyPath', {
type: 'string',
default: 'certs/privkey.pem',
2023-12-02 21:06:57 +01:00
describe: 'Path to your private key file.',
}).parseSync();
2023-07-20 19:32:15 +02:00
// change all relative paths
2024-03-15 14:30:13 +01:00
console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment.`);
const serverDirectory = __dirname;
2023-12-16 21:21:40 +01:00
process.chdir(serverDirectory);
2023-07-20 19:32:15 +02:00
const app = express();
app.use(compression());
app.use(responseTime());
const server_port = cliArguments.port ?? process.env.SILLY_TAVERN_PORT ?? getConfigValue('port', DEFAULT_PORT);
const autorun = (cliArguments.autorun ?? getConfigValue('autorun', DEFAULT_AUTORUN)) && !cliArguments.ssl;
const listen = cliArguments.listen ?? getConfigValue('listen', DEFAULT_LISTEN);
2024-03-30 21:46:18 +01:00
const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY);
const basicAuthMode = getConfigValue('basicAuthMode', false);
2023-07-20 19:32:15 +02:00
2024-03-19 23:39:48 +01:00
const { DIRECTORIES, UPLOADS_PATH } = require('./src/constants');
2023-07-20 19:32:15 +02:00
// CORS Settings //
const CORS = cors({
origin: 'null',
2023-12-02 21:06:57 +01:00
methods: ['OPTIONS'],
2023-07-20 19:32:15 +02:00
});
app.use(CORS);
2024-03-30 21:46:18 +01:00
if (listen && basicAuthMode) app.use(basicAuthMiddleware);
2023-07-20 19:32:15 +02:00
app.use(whitelistMiddleware(listen));
2023-07-20 19:32:15 +02:00
// CSRF Protection //
if (!cliArguments.disableCsrf) {
const CSRF_SECRET = crypto.randomBytes(8).toString('hex');
const COOKIES_SECRET = crypto.randomBytes(8).toString('hex');
const { generateToken, doubleCsrfProtection } = doubleCsrf({
getSecret: () => CSRF_SECRET,
cookieName: 'X-CSRF-Token',
cookieOptions: {
httpOnly: true,
sameSite: 'strict',
secure: false,
},
size: 64,
getTokenFromRequest: (req) => req.headers['x-csrf-token'],
});
app.get('/csrf-token', (req, res) => {
res.json({
'token': generateToken(res, req),
});
});
app.use(cookieParser(COOKIES_SECRET));
app.use(doubleCsrfProtection);
} else {
console.warn('\nCSRF protection is disabled. This will make your server vulnerable to CSRF attacks.\n');
app.get('/csrf-token', (req, res) => {
res.json({
'token': 'disabled',
});
});
}
if (enableCorsProxy) {
2023-12-02 14:32:09 +01:00
const bodyParser = require('body-parser');
app.use(bodyParser.json({
limit: '200mb',
}));
2023-11-25 20:56:57 +01:00
console.log('Enabling CORS proxy');
app.use('/proxy/:url(*)', async (req, res) => {
2023-11-25 20:56:57 +01:00
const url = req.params.url; // get the url from the request path
// Disallow circular requests
const serverUrl = req.protocol + '://' + req.get('host');
if (url.startsWith(serverUrl)) {
return res.status(400).send('Circular requests are not allowed');
}
try {
const headers = JSON.parse(JSON.stringify(req.headers));
delete headers['x-csrf-token'];
delete headers['host'];
delete headers['referer'];
delete headers['origin'];
delete headers['cookie'];
delete headers['sec-fetch-mode'];
delete headers['sec-fetch-site'];
delete headers['sec-fetch-dest'];
const bodyMethods = ['POST', 'PUT', 'PATCH'];
const response = await fetch(url, {
method: req.method,
headers: headers,
body: bodyMethods.includes(req.method) ? JSON.stringify(req.body) : undefined,
});
// Copy over relevant response params to the proxy response
forwardFetchResponse(response, res);
2023-11-25 20:56:57 +01:00
} catch (error) {
res.status(500).send('Error occurred while trying to proxy to: ' + url + ' ' + error);
}
});
2023-12-02 15:04:30 +01:00
} else {
app.use('/proxy/:url(*)', async (_, res) => {
const message = 'CORS proxy is disabled. Enable it in config.yaml or use the --corsProxy flag.';
console.log(message);
res.status(404).send(message);
});
2023-11-25 20:56:57 +01:00
}
2023-07-20 19:32:15 +02:00
2023-12-02 19:04:51 +01:00
app.use(express.static(process.cwd() + '/public', {}));
2023-07-20 19:32:15 +02:00
app.use('/backgrounds', (req, res) => {
2024-03-20 00:07:28 +01:00
const filePath = decodeURIComponent(path.join(process.cwd(), DIRECTORIES.backgrounds, req.url.replace(/%20/g, ' ')));
2023-07-20 19:32:15 +02:00
fs.readFile(filePath, (err, data) => {
if (err) {
res.status(404).send('File not found');
return;
}
//res.contentType('image/jpeg');
res.send(data);
});
});
app.use('/characters', (req, res) => {
const filePath = decodeURIComponent(path.join(process.cwd(), DIRECTORIES.characters, req.url.replace(/%20/g, ' ')));
2023-07-20 19:32:15 +02:00
fs.readFile(filePath, (err, data) => {
if (err) {
res.status(404).send('File not found');
return;
}
res.send(data);
});
});
2023-12-02 19:04:51 +01:00
app.use(multer({ dest: UPLOADS_PATH, limits: { fieldSize: 10 * 1024 * 1024 } }).single('avatar'));
app.get('/', function (request, response) {
response.sendFile(process.cwd() + '/public/index.html');
2023-07-20 19:32:15 +02:00
});
2023-09-17 13:27:41 +02:00
app.get('/version', async function (_, response) {
const data = await getVersion();
2023-07-20 19:32:15 +02:00
response.send(data);
2023-12-02 20:11:06 +01:00
});
2023-07-20 19:32:15 +02:00
2023-08-19 16:43:56 +02:00
function cleanUploads() {
try {
if (fs.existsSync(UPLOADS_PATH)) {
const uploads = fs.readdirSync(UPLOADS_PATH);
if (!uploads.length) {
return;
}
console.debug(`Cleaning uploads folder (${uploads.length} files)`);
uploads.forEach(file => {
const pathToFile = path.join(UPLOADS_PATH, file);
fs.unlinkSync(pathToFile);
});
}
} catch (err) {
console.error(err);
}
}
/**
* Redirect a deprecated API endpoint URL to its replacement. Because fetch, form submissions, and $.ajax follow
* redirects, this is transparent to client-side code.
* @param {string} src The URL to redirect from.
* @param {string} destination The URL to redirect to.
*/
function redirect(src, destination) {
app.use(src, (req, res) => {
console.warn(`API endpoint ${src} is deprecated; use ${destination} instead`);
// HTTP 301 causes the request to become a GET. 308 preserves the request method.
res.redirect(308, destination);
});
}
// Redirect deprecated character API endpoints
redirect('/createcharacter', '/api/characters/create');
redirect('/renamecharacter', '/api/characters/rename');
redirect('/editcharacter', '/api/characters/edit');
redirect('/editcharacterattribute', '/api/characters/edit-attribute');
redirect('/v2/editcharacterattribute', '/api/characters/merge-attributes');
redirect('/deletecharacter', '/api/characters/delete');
redirect('/getcharacters', '/api/characters/all');
redirect('/getonecharacter', '/api/characters/get');
redirect('/getallchatsofcharacter', '/api/characters/chats');
redirect('/importcharacter', '/api/characters/import');
redirect('/dupecharacter', '/api/characters/duplicate');
redirect('/exportcharacter', '/api/characters/export');
2023-12-06 18:11:57 +01:00
// Redirect deprecated chat API endpoints
redirect('/savechat', '/api/chats/save');
redirect('/getchat', '/api/chats/get');
redirect('/renamechat', '/api/chats/rename');
redirect('/delchat', '/api/chats/delete');
redirect('/exportchat', '/api/chats/export');
redirect('/importgroupchat', '/api/chats/group/import');
redirect('/importchat', '/api/chats/import');
redirect('/getgroupchat', '/api/chats/group/get');
redirect('/deletegroupchat', '/api/chats/group/delete');
redirect('/savegroupchat', '/api/chats/group/save');
2023-12-06 18:40:58 +01:00
// Redirect deprecated group API endpoints
redirect('/getgroups', '/api/groups/all');
redirect('/creategroup', '/api/groups/create');
redirect('/editgroup', '/api/groups/edit');
redirect('/deletegroup', '/api/groups/delete');
2023-12-06 23:09:48 +01:00
// Redirect deprecated worldinfo API endpoints
redirect('/getworldinfo', '/api/worldinfo/get');
redirect('/deleteworldinfo', '/api/worldinfo/delete');
redirect('/importworldinfo', '/api/worldinfo/import');
redirect('/editworldinfo', '/api/worldinfo/edit');
2023-12-07 18:31:34 +01:00
// Redirect deprecated stats API endpoints
redirect('/getstats', '/api/stats/get');
redirect('/recreatestats', '/api/stats/recreate');
redirect('/updatestats', '/api/stats/update');
2023-12-07 21:17:19 +01:00
// Redirect deprecated backgrounds API endpoints
redirect('/getbackgrounds', '/api/backgrounds/all');
redirect('/delbackground', '/api/backgrounds/delete');
redirect('/renamebackground', '/api/backgrounds/rename');
redirect('/downloadbackground', '/api/backgrounds/upload'); // yes, the downloadbackground endpoint actually uploads one
2024-03-19 23:14:32 +01:00
// Redirect deprecated theme API endpoints
redirect('/savetheme', '/api/themes/save');
2024-03-19 23:39:48 +01:00
// Redirect deprecated avatar API endpoints
redirect('/getuseravatars', '/api/avatars/get');
redirect('/deleteuseravatar', '/api/avatars/delete');
redirect('/uploaduseravatar', '/api/avatars/upload');
// Redirect deprecated quick reply endpoints
redirect('/deletequickreply', '/api/quick-replies/delete');
redirect('/savequickreply', '/api/quick-replies/save');
2024-03-19 23:59:06 +01:00
// Redirect deprecated image endpoints
redirect('/uploadimage', '/api/images/upload');
redirect('/listimgfiles/:folder', '/api/images/list/:folder');
2024-03-20 00:07:28 +01:00
// Redirect deprecated moving UI endpoints
redirect('/savemovingui', '/api/moving-ui/save');
// Moving UI
app.use('/api/moving-ui', require('./src/endpoints/moving-ui').router);
2024-03-19 23:59:06 +01:00
// Image management
app.use('/api/images', require('./src/endpoints/images').router);
// Quick reply management
app.use('/api/quick-replies', require('./src/endpoints/quick-replies').router);
2024-03-19 23:39:48 +01:00
// Avatar management
app.use('/api/avatars', require('./src/endpoints/avatars').router);
2024-03-19 23:14:32 +01:00
// Theme management
app.use('/api/themes', require('./src/endpoints/themes').router);
2023-11-06 20:47:00 +01:00
// OpenAI API
2023-12-04 18:53:17 +01:00
app.use('/api/openai', require('./src/endpoints/openai').router);
2023-11-06 20:47:00 +01:00
2023-12-14 13:37:53 +01:00
//Google API
app.use('/api/google', require('./src/endpoints/google').router);
2024-03-04 22:07:38 +01:00
//Anthropic API
app.use('/api/anthropic', require('./src/endpoints/anthropic').router);
// Tokenizers
app.use('/api/tokenizers', require('./src/endpoints/tokenizers').router);
// Preset management
app.use('/api/presets', require('./src/endpoints/presets').router);
// Secrets managemenet
app.use('/api/secrets', require('./src/endpoints/secrets').router);
// Thumbnail generation. These URLs are saved in chat, so this route cannot be renamed!
app.use('/thumbnail', require('./src/endpoints/thumbnails').router);
// NovelAI generation
app.use('/api/novelai', require('./src/endpoints/novelai').router);
// Third-party extensions
app.use('/api/extensions', require('./src/endpoints/extensions').router);
// Asset management
app.use('/api/assets', require('./src/endpoints/assets').router);
// File management
app.use('/api/files', require('./src/endpoints/files').router);
// Character management
app.use('/api/characters', require('./src/endpoints/characters').router);
2023-12-06 05:01:23 +01:00
// Chat management
app.use('/api/chats', require('./src/endpoints/chats').router);
// Group management
app.use('/api/groups', require('./src/endpoints/groups').router);
// World info management
app.use('/api/worldinfo', require('./src/endpoints/worldinfo').router);
// Stats calculation
2023-12-07 19:01:51 +01:00
const statsEndpoint = require('./src/endpoints/stats');
app.use('/api/stats', statsEndpoint.router);
// Background management
app.use('/api/backgrounds', require('./src/endpoints/backgrounds').router);
// Character sprite management
app.use('/api/sprites', require('./src/endpoints/sprites').router);
// Custom content management
app.use('/api/content', require('./src/endpoints/content-manager').router);
// Settings load/store
const settingsEndpoint = require('./src/endpoints/settings');
app.use('/api/settings', settingsEndpoint.router);
// Stable Diffusion generation
app.use('/api/sd', require('./src/endpoints/stable-diffusion').router);
// LLM and SD Horde generation
2023-12-04 18:51:02 +01:00
app.use('/api/horde', require('./src/endpoints/horde').router);
// Vector storage DB
app.use('/api/vector', require('./src/endpoints/vectors').router);
// Chat translation
app.use('/api/translate', require('./src/endpoints/translate').router);
// Emotion classification
app.use('/api/extra/classify', require('./src/endpoints/classify').router);
// Image captioning
app.use('/api/extra/caption', require('./src/endpoints/caption').router);
// Web search extension
app.use('/api/serpapi', require('./src/endpoints/serpapi').router);
// The different text generation APIs
// Ooba/OpenAI text completions
2023-12-13 01:22:35 +01:00
app.use('/api/backends/text-completions', require('./src/endpoints/backends/text-completions').router);
// KoboldAI
2023-12-14 00:37:51 +01:00
app.use('/api/backends/kobold', require('./src/endpoints/backends/kobold').router);
// OpenAI chat completions
app.use('/api/backends/chat-completions', require('./src/endpoints/backends/chat-completions').router);
// Scale (alt method)
app.use('/api/backends/scale-alt', require('./src/endpoints/backends/scale-alt').router);
// Speech (text-to-speech and speech-to-text)
app.use('/api/speech', require('./src/endpoints/speech').router);
2023-07-20 19:32:15 +02:00
const tavernUrl = new URL(
(cliArguments.ssl ? 'https://' : 'http://') +
(listen ? '0.0.0.0' : '127.0.0.1') +
2023-12-02 21:06:57 +01:00
(':' + server_port),
2023-07-20 19:32:15 +02:00
);
const autorunUrl = new URL(
(cliArguments.ssl ? 'https://' : 'http://') +
('127.0.0.1') +
2023-12-02 21:06:57 +01:00
(':' + server_port),
2023-07-20 19:32:15 +02:00
);
const setupTasks = async function () {
2023-09-17 13:27:41 +02:00
const version = await getVersion();
2023-07-20 19:32:15 +02:00
// Print formatted header
console.log();
console.log(`SillyTavern ${version.pkgVersion}`);
console.log(version.gitBranch ? `Running '${version.gitBranch}' (${version.gitRevision}) - ${version.commitDate}` : '');
if (version.gitBranch && !version.isLatest && ['staging', 'release'].includes(version.gitBranch)) {
console.log('INFO: Currently not on the latest commit.');
2024-04-03 00:00:20 +02:00
console.log(' Run \'git pull\' to update. If you have any merge conflicts, run \'git reset --hard\' and \'git pull\' to reset your branch.');
}
console.log();
2023-07-20 19:32:15 +02:00
// TODO: do endpoint init functions depend on certain directories existing or not existing? They should be callable
// in any order for encapsulation reasons, but right now it's unknown if that would break anything.
await settingsEndpoint.init();
2023-07-20 19:32:15 +02:00
ensurePublicDirectoriesExist();
await ensureThumbnailCache();
2023-07-21 14:28:32 +02:00
contentManager.checkForNewContent();
2023-08-19 16:43:56 +02:00
cleanUploads();
2023-07-20 19:32:15 +02:00
await loadTokenizers();
2023-12-07 19:01:51 +01:00
await statsEndpoint.init();
const cleanupPlugins = await loadPlugins();
2023-12-23 18:05:21 +01:00
const exitProcess = async () => {
2023-12-07 19:01:51 +01:00
statsEndpoint.onExit();
2023-12-23 18:03:13 +01:00
if (typeof cleanupPlugins === 'function') {
2023-12-23 18:05:21 +01:00
await cleanupPlugins();
2023-12-23 18:03:13 +01:00
}
2023-12-07 19:01:51 +01:00
process.exit();
};
2023-07-20 19:32:15 +02:00
// Set up event listeners for a graceful shutdown
2023-12-07 19:01:51 +01:00
process.on('SIGINT', exitProcess);
process.on('SIGTERM', exitProcess);
2023-07-20 19:32:15 +02:00
process.on('uncaughtException', (err) => {
console.error('Uncaught exception:', err);
2023-12-07 19:01:51 +01:00
exitProcess();
2023-07-20 19:32:15 +02:00
});
2023-12-16 21:21:40 +01:00
2023-07-20 19:32:15 +02:00
console.log('Launching...');
if (autorun) open(autorunUrl.toString());
2023-08-26 13:17:57 +02:00
console.log(color.green('SillyTavern is listening on: ' + tavernUrl));
2023-07-20 19:32:15 +02:00
if (listen) {
2023-11-25 22:45:33 +01:00
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');
2023-07-20 19:32:15 +02:00
}
2024-03-30 21:46:18 +01:00
if (basicAuthMode) {
const basicAuthUser = getConfigValue('basicAuthUser', {});
if (!basicAuthUser?.username || !basicAuthUser?.password) {
console.warn(color.yellow('Basic Authentication is enabled, but username or password is not set or empty!'));
}
}
2023-12-02 20:11:06 +01:00
};
2023-07-20 19:32:15 +02:00
2023-12-23 18:03:13 +01:00
/**
* Loads server plugins from a directory.
* @returns {Promise<Function>} Function to be run on server exit
*/
2023-12-16 21:21:40 +01:00
async function loadPlugins() {
try {
const pluginDirectory = path.join(serverDirectory, 'plugins');
const loader = require('./src/plugin-loader');
2023-12-23 18:03:13 +01:00
const cleanupPlugins = await loader.loadPlugins(app, pluginDirectory);
return cleanupPlugins;
2023-12-16 21:21:40 +01:00
} catch {
console.log('Plugin loading failed.');
2024-03-30 21:46:18 +01:00
return () => { };
2023-12-16 21:21:40 +01:00
}
}
2024-03-30 21:52:57 +01:00
if (listen && !getConfigValue('whitelistMode', true) && !basicAuthMode) {
2023-11-25 22:45:33 +01:00
if (getConfigValue('securityOverride', false)) {
2023-12-02 19:04:51 +01:00
console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.'));
2023-08-26 13:17:57 +02:00
}
2023-07-20 19:32:15 +02:00
else {
2023-08-26 13:17:57 +02:00
console.error(color.red('Your SillyTavern is currently unsecurely open to the public. Enable whitelisting or basic authentication.'));
2023-07-20 19:32:15 +02:00
process.exit(1);
}
}
2023-08-26 13:17:57 +02:00
if (cliArguments.ssl) {
2023-07-20 19:32:15 +02:00
https.createServer(
{
cert: fs.readFileSync(cliArguments.certPath),
2023-12-02 21:06:57 +01:00
key: fs.readFileSync(cliArguments.keyPath),
2023-07-20 19:32:15 +02:00
}, app)
.listen(
Number(tavernUrl.port) || 443,
2023-07-20 19:32:15 +02:00
tavernUrl.hostname,
2023-12-02 21:06:57 +01:00
setupTasks,
2023-07-20 19:32:15 +02:00
);
} else {
2023-07-20 19:32:15 +02:00
http.createServer(app).listen(
Number(tavernUrl.port) || 80,
2023-07-20 19:32:15 +02:00
tavernUrl.hostname,
2023-12-02 21:06:57 +01:00
setupTasks,
2023-07-20 19:32:15 +02:00
);
}
2023-07-20 19:32:15 +02:00
function ensurePublicDirectoriesExist() {
for (const dir of Object.values(DIRECTORIES)) {
2023-07-20 19:32:15 +02:00
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
}