mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-04-16 19:57:21 +02:00
Add server plugin loader
This commit is contained in:
parent
b569d74552
commit
16795dd5cc
@ -12,7 +12,7 @@ module.exports = {
|
|||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
// Server-side files (plus this configuration file)
|
// Server-side files (plus this configuration file)
|
||||||
files: ['src/**/*.js', './*.js'],
|
files: ['src/**/*.js', './*.js', 'plugins/**/*.js'],
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
},
|
},
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -42,3 +42,4 @@ access.log
|
|||||||
/vectors/
|
/vectors/
|
||||||
/cache/
|
/cache/
|
||||||
public/css/user.css
|
public/css/user.css
|
||||||
|
/plugins/
|
||||||
|
@ -58,3 +58,5 @@ openai:
|
|||||||
deepl:
|
deepl:
|
||||||
# Available options: default, more, less, prefer_more, prefer_less
|
# Available options: default, more, less, prefer_more, prefer_less
|
||||||
formality: default
|
formality: default
|
||||||
|
# -- SERVER PLUGIN CONFIGURATION --
|
||||||
|
enableServerPlugins: false
|
||||||
|
0
plugins/.gitkeep
Normal file
0
plugins/.gitkeep
Normal file
16
server.js
16
server.js
@ -93,9 +93,9 @@ const cliArguments = yargs(hideBin(process.argv))
|
|||||||
}).parseSync();
|
}).parseSync();
|
||||||
|
|
||||||
// change all relative paths
|
// change all relative paths
|
||||||
const directory = process['pkg'] ? path.dirname(process.execPath) : __dirname;
|
const serverDirectory = process['pkg'] ? path.dirname(process.execPath) : __dirname;
|
||||||
console.log(process['pkg'] ? 'Running from binary' : 'Running from source');
|
console.log(process['pkg'] ? 'Running from binary' : 'Running from source');
|
||||||
process.chdir(directory);
|
process.chdir(serverDirectory);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
@ -621,6 +621,8 @@ const setupTasks = async function () {
|
|||||||
exitProcess();
|
exitProcess();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await loadPlugins();
|
||||||
|
|
||||||
console.log('Launching...');
|
console.log('Launching...');
|
||||||
|
|
||||||
if (autorun) open(autorunUrl.toString());
|
if (autorun) open(autorunUrl.toString());
|
||||||
@ -632,6 +634,16 @@ const setupTasks = async function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function loadPlugins() {
|
||||||
|
try {
|
||||||
|
const pluginDirectory = path.join(serverDirectory, 'plugins');
|
||||||
|
const loader = require('./src/plugin-loader');
|
||||||
|
await loader.loadPlugins(app, pluginDirectory);
|
||||||
|
} catch {
|
||||||
|
console.log('Plugin loading failed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (listen && !getConfigValue('whitelistMode', true) && !getConfigValue('basicAuthMode', false)) {
|
if (listen && !getConfigValue('whitelistMode', true) && !getConfigValue('basicAuthMode', false)) {
|
||||||
if (getConfigValue('securityOverride', false)) {
|
if (getConfigValue('securityOverride', false)) {
|
||||||
console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.'));
|
console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.'));
|
||||||
|
161
src/plugin-loader.js
Normal file
161
src/plugin-loader.js
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { getConfigValue } = require('./util');
|
||||||
|
const enableServerPlugins = getConfigValue('enableServerPlugins', false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a file is a CommonJS module.
|
||||||
|
* @param {string} file Path to file
|
||||||
|
* @returns {boolean} True if file is a CommonJS module
|
||||||
|
*/
|
||||||
|
const isCommonJS = (file) => path.extname(file) === '.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a file is an ECMAScript module.
|
||||||
|
* @param {string} file Path to file
|
||||||
|
* @returns {boolean} True if file is an ECMAScript module
|
||||||
|
*/
|
||||||
|
const isESModule = (file) => path.extname(file) === '.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load and initialize server plugins from a directory if they are enabled.
|
||||||
|
* @param {import('express').Express} app Express app
|
||||||
|
* @param {string} pluginsPath Path to plugins directory
|
||||||
|
* @returns {Promise<any>} Promise that resolves when all plugins are loaded
|
||||||
|
*/
|
||||||
|
async function loadPlugins(app, pluginsPath) {
|
||||||
|
// Server plugins are disabled.
|
||||||
|
if (!enableServerPlugins) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugins directory does not exist.
|
||||||
|
if (!fs.existsSync(pluginsPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = fs.readdirSync(pluginsPath);
|
||||||
|
|
||||||
|
// No plugins to load.
|
||||||
|
if (files.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const pluginFilePath = path.join(pluginsPath, file);
|
||||||
|
|
||||||
|
if (fs.statSync(pluginFilePath).isDirectory()) {
|
||||||
|
await loadFromDirectory(app, pluginFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a JavaScript file.
|
||||||
|
if (!isCommonJS(file) && !isESModule(file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadFromFile(app, pluginFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadFromDirectory(app, pluginDirectoryPath) {
|
||||||
|
const files = fs.readdirSync(pluginDirectoryPath);
|
||||||
|
|
||||||
|
// No plugins to load.
|
||||||
|
if (files.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin is an npm package.
|
||||||
|
const packageJsonFilePath = path.join(pluginDirectoryPath, 'package.json');
|
||||||
|
if (fs.existsSync(packageJsonFilePath)) {
|
||||||
|
if (await loadFromPackage(app, packageJsonFilePath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin is a CommonJS module.
|
||||||
|
const cjsFilePath = path.join(pluginDirectoryPath, 'index.js');
|
||||||
|
if (fs.existsSync(cjsFilePath)) {
|
||||||
|
if (await loadFromFile(app, cjsFilePath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin is an ECMAScript module.
|
||||||
|
const esmFilePath = path.join(pluginDirectoryPath, 'index.mjs');
|
||||||
|
if (fs.existsSync(esmFilePath)) {
|
||||||
|
if (await loadFromFile(app, esmFilePath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and initializes a plugin from an npm package.
|
||||||
|
* @param {import('express').Express} app Express app
|
||||||
|
* @param {string} packageJsonPath Path to package.json file
|
||||||
|
* @returns {Promise<boolean>} Promise that resolves to true if plugin was loaded successfully
|
||||||
|
*/
|
||||||
|
async function loadFromPackage(app, packageJsonPath) {
|
||||||
|
try {
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||||
|
if (packageJson.main) {
|
||||||
|
const pluginFilePath = path.join(path.dirname(packageJsonPath), packageJson.main);
|
||||||
|
return await loadFromFile(app, pluginFilePath);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to load plugin from ${packageJsonPath}: ${error}`);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and initializes a plugin from a file.
|
||||||
|
* @param {import('express').Express} app Express app
|
||||||
|
* @param {string} pluginFilePath Path to plugin directory
|
||||||
|
* @returns {Promise<boolean>} Promise that resolves to true if plugin was loaded successfully
|
||||||
|
*/
|
||||||
|
async function loadFromFile(app, pluginFilePath) {
|
||||||
|
try {
|
||||||
|
const plugin = await getPluginModule(pluginFilePath);
|
||||||
|
console.log(`Initializing plugin from ${pluginFilePath}`);
|
||||||
|
return await initPlugin(app, plugin);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to load plugin from ${pluginFilePath}: ${error}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a plugin module.
|
||||||
|
* @param {import('express').Express} app Express app
|
||||||
|
* @param {any} plugin Plugin module
|
||||||
|
* @returns {Promise<boolean>} Promise that resolves to true if plugin was initialized successfully
|
||||||
|
*/
|
||||||
|
async function initPlugin(app, plugin) {
|
||||||
|
if (typeof plugin.init === 'function') {
|
||||||
|
await plugin.init(app);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a module from a file depending on the module type.
|
||||||
|
* @param {string} pluginFilePath Path to plugin file
|
||||||
|
* @returns {Promise<any>} Promise that resolves to plugin module
|
||||||
|
*/
|
||||||
|
async function getPluginModule(pluginFilePath) {
|
||||||
|
if (isCommonJS(pluginFilePath)) {
|
||||||
|
return require(pluginFilePath);
|
||||||
|
}
|
||||||
|
if (isESModule(pluginFilePath)) {
|
||||||
|
return await import(pluginFilePath);
|
||||||
|
}
|
||||||
|
throw new Error(`Unsupported module type in ${pluginFilePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
loadPlugins,
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user