mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-04-07 14:04:35 +08:00
[bug] Fix startup failure when scripts/engine/... is missing; Fix bug 75169
This commit is contained in:
@ -42,10 +42,10 @@ const vm = require('vm');
|
||||
|
||||
// Configuration constants
|
||||
const cfgAiApiTimeout = config.get('aiSettings.timeout');
|
||||
const cfgAiApiModels = config.get('aiSettings.models');
|
||||
const cfgAiApiActions = config.get('aiSettings.actions');
|
||||
const cfgAiPluginDir = config.get('aiSettings.pluginDir');
|
||||
|
||||
const engineScriptsDir = path.join(cfgAiPluginDir, 'scripts/engine');
|
||||
|
||||
function setCtx(ctx) {
|
||||
sandbox.ctx = ctx;
|
||||
console.log = ctx.logger.debug.bind(ctx.logger);//todo make default in logger
|
||||
@ -55,14 +55,29 @@ function setCtx(ctx) {
|
||||
// Set up the environment for the client-side engine.js
|
||||
const sandbox = {
|
||||
ctx: null,
|
||||
window: {AI: {}},
|
||||
window: {
|
||||
AI: {
|
||||
TmpProviderForModels: null,
|
||||
Providers: {},
|
||||
InternalProviders: [],
|
||||
_getHeaders: function() {return {};},
|
||||
serializeProviders: function() {return [];},
|
||||
ActionsGetSorted: function() {return [];},
|
||||
getModels: function() {return [];},
|
||||
onLoadInternalProviders: function() {},
|
||||
Storage: {
|
||||
serializeModels: function() {return [];}
|
||||
},
|
||||
CapabilitiesUI: {}
|
||||
}
|
||||
},
|
||||
Asc: {
|
||||
plugin: {
|
||||
tr: function(text) {
|
||||
// Just return the original text in the stub
|
||||
return text;
|
||||
tr: function(text) {
|
||||
// Just return the original text in the stub
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -89,7 +104,7 @@ const sandbox = {
|
||||
options.headers || {},
|
||||
options.body || null,
|
||||
timeoutOptions,
|
||||
10 * 1024 * 1024,
|
||||
null,
|
||||
false
|
||||
)
|
||||
.then(async (result) => {
|
||||
@ -110,7 +125,7 @@ const sandbox = {
|
||||
};
|
||||
|
||||
// Initialize minimal AI object with required functionality
|
||||
const AI = sandbox.AI = sandbox.window.AI;
|
||||
sandbox.AI = sandbox.window.AI;
|
||||
setCtx(operationContext.global);
|
||||
|
||||
/**
|
||||
@ -118,8 +133,15 @@ setCtx(operationContext.global);
|
||||
*/
|
||||
function loadInternalProviders() {
|
||||
// Add simple provider loading logic
|
||||
const enginePath = path.join(cfgAiPluginDir, 'scripts/engine/providers/internal');
|
||||
const enginePath = path.join(engineScriptsDir, 'providers/internal');
|
||||
|
||||
// Check if the providers directory exists before trying to read it
|
||||
if (!fs.existsSync(enginePath)) {
|
||||
sandbox.ctx.logger.warn('Internal providers directory not found:', enginePath);
|
||||
sandbox.AI.onLoadInternalProviders();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read providers directory
|
||||
const files = fs.readdirSync(enginePath);
|
||||
@ -150,27 +172,28 @@ function loadInternalProviders() {
|
||||
sandbox.ctx.logger.error('Error loading internal providers:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Simple loadInternalProviders implementation
|
||||
*/
|
||||
function fillConfigObjects() {
|
||||
AI.Models = cfgAiApiModels;
|
||||
for(let id in cfgAiApiActions) {
|
||||
let action = cfgAiApiActions[id];
|
||||
if (action.model && AI.Actions[id]) {
|
||||
AI.Actions[id].model = action.model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load engine.js
|
||||
let engineCode = '';
|
||||
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/storage.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/local_storage.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/providers/base.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/providers/provider.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/engine.js'), 'utf8');
|
||||
vm.runInNewContext(engineCode, sandbox);
|
||||
|
||||
try {
|
||||
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'storage.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'local_storage.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'providers/base.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'providers/provider.js'), 'utf8');
|
||||
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'engine.js'), 'utf8');
|
||||
} catch (error) {
|
||||
sandbox.ctx.logger.warn('Error reading engine script files:', error);
|
||||
}
|
||||
|
||||
// Run engine code if available
|
||||
if (engineCode) {
|
||||
try {
|
||||
vm.runInNewContext(engineCode, sandbox);
|
||||
} catch (error) {
|
||||
sandbox.ctx.logger.error('Error executing engine scripts:', error);
|
||||
}
|
||||
}
|
||||
|
||||
//start from engine/register.js
|
||||
(function() {
|
||||
@ -240,7 +263,6 @@ vm.runInNewContext(engineCode, sandbox);
|
||||
|
||||
sandbox.AI.loadInternalProviders = loadInternalProviders;
|
||||
loadInternalProviders();
|
||||
fillConfigObjects();
|
||||
|
||||
exports.setCtx = setCtx;
|
||||
exports.AI = sandbox.AI;
|
||||
|
||||
@ -33,7 +33,6 @@
|
||||
'use strict';
|
||||
|
||||
const { pipeline } = require('stream/promises');
|
||||
const { buffer } = require('node:stream/consumers');
|
||||
const config = require('config');
|
||||
const utils = require('./../../../Common/sources/utils');
|
||||
const operationContext = require('./../../../Common/sources/operationContext');
|
||||
@ -45,12 +44,10 @@ const aiEngine = require('./aiEngineWrapper');
|
||||
|
||||
const cfgAiApiAllowedOrigins = config.get('aiSettings.allowedCorsOrigins');
|
||||
const cfgAiApiTimeout = config.get('aiSettings.timeout');
|
||||
const cfgAiApiCache = config.get('aiSettings.cache');
|
||||
const cfgTokenEnableBrowser = config.get('services.CoAuthoring.token.enable.browser');
|
||||
const cfgAiSettings = config.get('aiSettings');
|
||||
|
||||
const AI = aiEngine.AI;
|
||||
const nodeCache = new utils.NodeCache(cfgAiApiCache);
|
||||
/**
|
||||
* Helper function to set CORS headers if the request origin is allowed
|
||||
*
|
||||
@ -103,7 +100,7 @@ function handleCorsHeaders(req, res, ctx, handleOptions = true) {
|
||||
|
||||
/**
|
||||
* Makes an HTTP request to an AI API endpoint using the provided request and response objects
|
||||
*
|
||||
*
|
||||
* @param {object} req - Express request object
|
||||
* @param {object} res - Express response object
|
||||
* @returns {Promise<void>} - Promise resolving when the request is complete
|
||||
@ -142,10 +139,6 @@ async function proxyRequest(req, res) {
|
||||
wholeCycle: tenAiApiTimeout
|
||||
};
|
||||
|
||||
// Get request size limit if configured
|
||||
const sizeLimit = 10 * 1024 * 1024; // Default to 10MB
|
||||
|
||||
|
||||
let providerHeaders;
|
||||
// Determine which API key to use based on the target URL
|
||||
if (body.target) {
|
||||
@ -171,21 +164,13 @@ async function proxyRequest(req, res) {
|
||||
headers,
|
||||
body: body.data,
|
||||
timeout: timeoutOptions,
|
||||
limit: sizeLimit,
|
||||
limit: null,
|
||||
filterPrivate: false
|
||||
};
|
||||
|
||||
// Create a safe copy for logging without sensitive info
|
||||
const safeLogParams = { ...requestParams };
|
||||
// if (safeLogParams.headers) {
|
||||
// safeLogParams.headers = { ...safeLogParams.headers };
|
||||
// if (safeLogParams.headers.Authorization) {
|
||||
// safeLogParams.headers.Authorization = '[REDACTED]';
|
||||
// }
|
||||
// }
|
||||
|
||||
// Log the sanitized request parameters
|
||||
ctx.logger.debug(`Proxying request: %j`, safeLogParams);
|
||||
ctx.logger.debug(`Proxying request: %j`, requestParams);
|
||||
|
||||
// Use utils.httpRequest to make the request
|
||||
const result = await utils.httpRequest(
|
||||
@ -221,9 +206,9 @@ async function proxyRequest(req, res) {
|
||||
}
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
ctx.logger.info('End proxyRequest');
|
||||
}
|
||||
} finally {
|
||||
ctx.logger.info('End proxyRequest');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,7 +228,7 @@ async function processProvider(ctx, provider) {
|
||||
let engineModelsUI = [];
|
||||
try {
|
||||
// Call getModels from engine.js
|
||||
if (provider.key) {
|
||||
if (provider.key && AI.Providers[provider.name]) {
|
||||
AI.Providers[provider.name].key = provider.key;
|
||||
// aiEngine.setCtx(ctx);
|
||||
// await AI.getModels(provider);
|
||||
@ -275,11 +260,6 @@ async function processProvider(ctx, provider) {
|
||||
async function getPluginSettings(ctx) {
|
||||
const logger = ctx.logger;
|
||||
logger.info('Starting getPluginSettings');
|
||||
let res = nodeCache.get(ctx.tenant);
|
||||
if (res) {
|
||||
ctx.logger.debug('getPluginSettings from cache');
|
||||
return res;
|
||||
}
|
||||
const result = {
|
||||
version: 3,
|
||||
actions: {},
|
||||
@ -337,7 +317,6 @@ async function getPluginSettings(ctx) {
|
||||
}
|
||||
const tenVersion = ctx.getCfg('aiSettings.version', cfgAiSettings.version);
|
||||
result.version = tenVersion;
|
||||
// nodeCache.set(ctx.tenant, result);
|
||||
} catch (error) {
|
||||
logger.error('Error retrieving AI models from config:', error);
|
||||
}
|
||||
@ -392,9 +371,9 @@ async function requestModels(req, res) {
|
||||
if (AI.Providers[body.name]) {
|
||||
AI.Providers[body.name].key = body.key;
|
||||
AI.Providers[body.name].url = body.url;
|
||||
}
|
||||
}
|
||||
let getRes = await AI.getModels(body);
|
||||
getRes.modelsApi = AI.TmpProviderForModels?.models;
|
||||
getRes.modelsApi = AI.TmpProviderForModels?.models;
|
||||
res.json(getRes);
|
||||
} catch (error) {
|
||||
ctx.logger.error('getModels error: %s', error.stack);
|
||||
|
||||
Reference in New Issue
Block a user