diff --git a/api/models/index.js b/api/models/index.js index 048e270e11..7f2c651941 100644 --- a/api/models/index.js +++ b/api/models/index.js @@ -24,8 +24,15 @@ const { getConvoTitle, getConvo, saveConvo, deleteConvos } = require('./Conversa const { getPreset, getPresets, savePreset, deletePresets } = require('./Preset'); const { File } = require('~/db/models'); +const seedDatabase = async () => { + await methods.initializeRoles(); + await methods.seedDefaultRoles(); + await methods.ensureDefaultCategories(); +}; + module.exports = { ...methods, + seedDatabase, comparePassword, findFileById, createFile, diff --git a/api/models/interface.js b/api/models/interface.js new file mode 100644 index 0000000000..a79a8e747f --- /dev/null +++ b/api/models/interface.js @@ -0,0 +1,24 @@ +const { logger } = require('@librechat/data-schemas'); +const { updateInterfacePermissions: updateInterfacePerms } = require('@librechat/api'); +const { getRoleByName, updateAccessPermissions } = require('./Role'); + +/** + * Update interface permissions based on app configuration. + * Must be done independently from loading the app config. + * @param {AppConfig} appConfig + */ +async function updateInterfacePermissions(appConfig) { + try { + await updateInterfacePerms({ + appConfig, + getRoleByName, + updateAccessPermissions, + }); + } catch (error) { + logger.error('Error updating interface permissions:', error); + } +} + +module.exports = { + updateInterfacePermissions, +}; diff --git a/api/server/index.js b/api/server/index.js index 1c9f571468..c28418b869 100644 --- a/api/server/index.js +++ b/api/server/index.js @@ -14,13 +14,14 @@ const { isEnabled, ErrorController } = require('@librechat/api'); const { connectDb, indexSync } = require('~/db'); const validateImageRequest = require('./middleware/validateImageRequest'); const { jwtLogin, ldapLogin, passportLogin } = require('~/strategies'); +const { updateInterfacePermissions } = require('~/models/interface'); const { checkMigrations } = require('./services/start/migration'); const initializeMCPs = require('./services/initializeMCPs'); const configureSocialLogins = require('./socialLogins'); const { getAppConfig } = require('./services/Config'); -const AppService = require('./services/AppService'); const staticCache = require('./utils/staticCache'); const noIndex = require('./middleware/noIndex'); +const { seedDatabase } = require('~/models'); const routes = require('./routes'); const { PORT, HOST, ALLOW_SOCIAL_LOGIN, DISABLE_COMPRESSION, TRUST_PROXY } = process.env ?? {}; @@ -46,8 +47,10 @@ const startServer = async () => { app.disable('x-powered-by'); app.set('trust proxy', trusted_proxy); - await AppService(); + await seedDatabase(); + const appConfig = await getAppConfig(); + await updateInterfacePermissions(appConfig); const indexPath = path.join(appConfig.paths.dist, 'index.html'); const indexHTML = fs.readFileSync(indexPath, 'utf8'); diff --git a/api/server/services/AppService.js b/api/server/services/AppService.js index bafeec66a8..eaaf47894c 100644 --- a/api/server/services/AppService.js +++ b/api/server/services/AppService.js @@ -3,6 +3,7 @@ const { loadMemoryConfig, agentsConfigSetup, loadWebSearchConfig, + loadDefaultInterface, } = require('@librechat/api'); const { FileSources, @@ -16,16 +17,14 @@ const { checkHealth, checkConfig, } = require('./start/checks'); -const { ensureDefaultCategories, seedDefaultRoles, initializeRoles } = require('~/models'); -const { setCachedTools, setAppConfig, loadCustomConfig } = require('./Config'); const { initializeAzureBlobService } = require('./Files/Azure/initialize'); const { initializeFirebase } = require('./Files/Firebase/initialize'); const handleRateLimits = require('./Config/handleRateLimits'); -const { loadDefaultInterface } = require('./start/interface'); +const loadCustomConfig = require('./Config/loadCustomConfig'); const { loadTurnstileConfig } = require('./start/turnstile'); const { processModelSpecs } = require('./start/modelSpecs'); const { initializeS3 } = require('./Files/S3/initialize'); -const { loadAndFormatTools } = require('./ToolService'); +const { loadAndFormatTools } = require('./start/tools'); const { loadEndpoints } = require('./start/endpoints'); const paths = require('~/config/paths'); @@ -34,9 +33,6 @@ const paths = require('~/config/paths'); * @function AppService */ const AppService = async () => { - await initializeRoles(); - await seedDefaultRoles(); - await ensureDefaultCategories(); /** @type {TCustomConfig} */ const config = (await loadCustomConfig()) ?? {}; const configDefaults = getConfigDefaults(); @@ -73,17 +69,11 @@ const AppService = async () => { adminFilter: filteredTools, adminIncluded: includedTools, directory: paths.structuredTools, - imageOutputType, - fileStrategy, }); - await setCachedTools(availableTools, { isGlobal: true }); - - // Store MCP config for later initialization const mcpConfig = config.mcpServers || null; - const registration = config.registration ?? configDefaults.registration; - const interfaceConfig = await loadDefaultInterface(config, configDefaults); + const interfaceConfig = await loadDefaultInterface({ config, configDefaults }); const turnstileConfig = loadTurnstileConfig(config, configDefaults); const speech = config.speech; @@ -100,6 +90,7 @@ const AppService = async () => { registration, filteredTools, includedTools, + availableTools, imageOutputType, interfaceConfig, turnstileConfig, @@ -115,8 +106,7 @@ const AppService = async () => { [EModelEndpoint.agents]: agentsDefaults, }, }; - await setAppConfig(appConfig); - return; + return appConfig; } checkConfig(config); @@ -131,7 +121,7 @@ const AppService = async () => { endpoints: loadedEndpoints, }; - await setAppConfig(appConfig); + return appConfig; }; module.exports = AppService; diff --git a/api/server/services/Config/app.js b/api/server/services/Config/app.js index 03c90293da..e357b55d9f 100644 --- a/api/server/services/Config/app.js +++ b/api/server/services/Config/app.js @@ -1,5 +1,7 @@ const { logger } = require('@librechat/data-schemas'); const { CacheKeys } = require('librechat-data-provider'); +const AppService = require('~/server/services/AppService'); +const { setCachedTools } = require('./getCachedTools'); const getLogStores = require('~/cache/getLogStores'); /** @@ -22,9 +24,20 @@ async function getAppConfig(options = {}) { } } - const baseConfig = await cache.get(CacheKeys.APP_CONFIG); + let baseConfig = await cache.get(CacheKeys.APP_CONFIG); if (!baseConfig) { - throw new Error('App configuration not initialized. Please ensure AppService has been called.'); + logger.info('[getAppConfig] App configuration not initialized. Initializing AppService...'); + baseConfig = await AppService(); + + if (!baseConfig) { + throw new Error('Failed to initialize app configuration through AppService.'); + } + + if (baseConfig.availableTools) { + await setCachedTools(baseConfig.availableTools, { isGlobal: true }); + } + + await cache.set(CacheKeys.APP_CONFIG, baseConfig); } // For now, return the base config @@ -49,19 +62,7 @@ async function clearAppConfigCache() { return await cache.delete(cacheKey); } -/** - * Initialize the app configuration during startup - * @param {AppConfig} config - The initial configuration to store - * @returns {Promise} - */ -async function setAppConfig(config) { - const cache = getLogStores(CacheKeys.CONFIG_STORE); - await cache.set(CacheKeys.APP_CONFIG, config); - logger.debug('[getAppConfig] App configuration initialized'); -} - module.exports = { getAppConfig, - setAppConfig, clearAppConfigCache, };