@intranefr/superbackend 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.env.example +15 -0
  2. package/README.md +11 -0
  3. package/analysis-only.skill +0 -0
  4. package/index.js +23 -0
  5. package/package.json +8 -2
  6. package/src/admin/endpointRegistry.js +120 -0
  7. package/src/controllers/admin.controller.js +90 -6
  8. package/src/controllers/adminBlockDefinitions.controller.js +127 -0
  9. package/src/controllers/adminBlockDefinitionsAi.controller.js +54 -0
  10. package/src/controllers/adminCache.controller.js +342 -0
  11. package/src/controllers/adminContextBlockDefinitions.controller.js +141 -0
  12. package/src/controllers/adminCrons.controller.js +388 -0
  13. package/src/controllers/adminDbBrowser.controller.js +124 -0
  14. package/src/controllers/adminEjsVirtual.controller.js +13 -3
  15. package/src/controllers/adminExperiments.controller.js +200 -0
  16. package/src/controllers/adminHeadless.controller.js +9 -2
  17. package/src/controllers/adminHealthChecks.controller.js +570 -0
  18. package/src/controllers/adminI18n.controller.js +51 -29
  19. package/src/controllers/adminLlm.controller.js +126 -2
  20. package/src/controllers/adminPages.controller.js +720 -0
  21. package/src/controllers/adminPagesContextBlocksAi.controller.js +54 -0
  22. package/src/controllers/adminProxy.controller.js +113 -0
  23. package/src/controllers/adminRateLimits.controller.js +138 -0
  24. package/src/controllers/adminRbac.controller.js +803 -0
  25. package/src/controllers/adminScripts.controller.js +126 -4
  26. package/src/controllers/adminSeoConfig.controller.js +71 -48
  27. package/src/controllers/blogAdmin.controller.js +279 -0
  28. package/src/controllers/blogAiAdmin.controller.js +224 -0
  29. package/src/controllers/blogAutomationAdmin.controller.js +141 -0
  30. package/src/controllers/blogInternal.controller.js +26 -0
  31. package/src/controllers/blogPublic.controller.js +89 -0
  32. package/src/controllers/experiments.controller.js +85 -0
  33. package/src/controllers/fileManager.controller.js +190 -0
  34. package/src/controllers/fileManagerStoragePolicy.controller.js +23 -0
  35. package/src/controllers/healthChecksPublic.controller.js +196 -0
  36. package/src/controllers/internalExperiments.controller.js +17 -0
  37. package/src/controllers/metrics.controller.js +64 -4
  38. package/src/controllers/orgAdmin.controller.js +80 -0
  39. package/src/helpers/mongooseHelper.js +258 -0
  40. package/src/helpers/scriptBase.js +230 -0
  41. package/src/helpers/scriptRunner.js +335 -0
  42. package/src/middleware/rbac.js +62 -0
  43. package/src/middleware.js +810 -48
  44. package/src/models/BlockDefinition.js +27 -0
  45. package/src/models/BlogAutomationLock.js +14 -0
  46. package/src/models/BlogAutomationRun.js +39 -0
  47. package/src/models/BlogPost.js +42 -0
  48. package/src/models/CacheEntry.js +26 -0
  49. package/src/models/ConsoleEntry.js +32 -0
  50. package/src/models/ConsoleLog.js +23 -0
  51. package/src/models/ContextBlockDefinition.js +33 -0
  52. package/src/models/CronExecution.js +47 -0
  53. package/src/models/CronJob.js +70 -0
  54. package/src/models/Experiment.js +75 -0
  55. package/src/models/ExperimentAssignment.js +23 -0
  56. package/src/models/ExperimentEvent.js +26 -0
  57. package/src/models/ExperimentMetricBucket.js +30 -0
  58. package/src/models/ExternalDbConnection.js +49 -0
  59. package/src/models/FileEntry.js +22 -0
  60. package/src/models/GlobalSetting.js +1 -2
  61. package/src/models/HealthAutoHealAttempt.js +57 -0
  62. package/src/models/HealthCheck.js +132 -0
  63. package/src/models/HealthCheckRun.js +51 -0
  64. package/src/models/HealthIncident.js +49 -0
  65. package/src/models/Page.js +95 -0
  66. package/src/models/PageCollection.js +42 -0
  67. package/src/models/ProxyEntry.js +66 -0
  68. package/src/models/RateLimitCounter.js +19 -0
  69. package/src/models/RateLimitMetricBucket.js +20 -0
  70. package/src/models/RbacGrant.js +25 -0
  71. package/src/models/RbacGroup.js +16 -0
  72. package/src/models/RbacGroupMember.js +13 -0
  73. package/src/models/RbacGroupRole.js +13 -0
  74. package/src/models/RbacRole.js +25 -0
  75. package/src/models/RbacUserRole.js +13 -0
  76. package/src/models/ScriptDefinition.js +1 -0
  77. package/src/models/Webhook.js +2 -0
  78. package/src/routes/admin.routes.js +2 -0
  79. package/src/routes/adminBlog.routes.js +21 -0
  80. package/src/routes/adminBlogAi.routes.js +16 -0
  81. package/src/routes/adminBlogAutomation.routes.js +27 -0
  82. package/src/routes/adminCache.routes.js +20 -0
  83. package/src/routes/adminConsoleManager.routes.js +302 -0
  84. package/src/routes/adminCrons.routes.js +25 -0
  85. package/src/routes/adminDbBrowser.routes.js +65 -0
  86. package/src/routes/adminEjsVirtual.routes.js +2 -1
  87. package/src/routes/adminExperiments.routes.js +29 -0
  88. package/src/routes/adminHeadless.routes.js +2 -1
  89. package/src/routes/adminHealthChecks.routes.js +28 -0
  90. package/src/routes/adminI18n.routes.js +4 -3
  91. package/src/routes/adminLlm.routes.js +4 -2
  92. package/src/routes/adminPages.routes.js +55 -0
  93. package/src/routes/adminProxy.routes.js +15 -0
  94. package/src/routes/adminRateLimits.routes.js +17 -0
  95. package/src/routes/adminRbac.routes.js +38 -0
  96. package/src/routes/adminSeoConfig.routes.js +5 -4
  97. package/src/routes/adminUiComponents.routes.js +2 -1
  98. package/src/routes/blogInternal.routes.js +14 -0
  99. package/src/routes/blogPublic.routes.js +9 -0
  100. package/src/routes/experiments.routes.js +30 -0
  101. package/src/routes/fileManager.routes.js +62 -0
  102. package/src/routes/fileManagerStoragePolicy.routes.js +9 -0
  103. package/src/routes/healthChecksPublic.routes.js +9 -0
  104. package/src/routes/internalExperiments.routes.js +15 -0
  105. package/src/routes/log.routes.js +43 -60
  106. package/src/routes/metrics.routes.js +4 -2
  107. package/src/routes/orgAdmin.routes.js +1 -0
  108. package/src/routes/pages.routes.js +123 -0
  109. package/src/routes/proxy.routes.js +46 -0
  110. package/src/routes/rbac.routes.js +47 -0
  111. package/src/routes/webhook.routes.js +2 -1
  112. package/src/routes/workflows.routes.js +4 -0
  113. package/src/services/blockDefinitionsAi.service.js +247 -0
  114. package/src/services/blog.service.js +99 -0
  115. package/src/services/blogAutomation.service.js +978 -0
  116. package/src/services/blogCronsBootstrap.service.js +185 -0
  117. package/src/services/blogPublishing.service.js +58 -0
  118. package/src/services/cacheLayer.service.js +696 -0
  119. package/src/services/consoleManager.service.js +738 -0
  120. package/src/services/consoleOverride.service.js +7 -1
  121. package/src/services/cronScheduler.service.js +350 -0
  122. package/src/services/dbBrowser.service.js +536 -0
  123. package/src/services/ejsVirtual.service.js +102 -32
  124. package/src/services/experiments.service.js +273 -0
  125. package/src/services/experimentsAggregation.service.js +308 -0
  126. package/src/services/experimentsCronsBootstrap.service.js +118 -0
  127. package/src/services/experimentsRetention.service.js +43 -0
  128. package/src/services/experimentsWs.service.js +134 -0
  129. package/src/services/fileManager.service.js +475 -0
  130. package/src/services/fileManagerStoragePolicy.service.js +285 -0
  131. package/src/services/globalSettings.service.js +15 -0
  132. package/src/services/healthChecks.service.js +650 -0
  133. package/src/services/healthChecksBootstrap.service.js +109 -0
  134. package/src/services/healthChecksScheduler.service.js +106 -0
  135. package/src/services/jsonConfigs.service.js +2 -2
  136. package/src/services/llmDefaults.service.js +190 -0
  137. package/src/services/migrationAssets/s3.js +2 -2
  138. package/src/services/pages.service.js +602 -0
  139. package/src/services/pagesContext.service.js +331 -0
  140. package/src/services/pagesContextBlocksAi.service.js +349 -0
  141. package/src/services/proxy.service.js +535 -0
  142. package/src/services/rateLimiter.service.js +623 -0
  143. package/src/services/rbac.service.js +212 -0
  144. package/src/services/scriptsRunner.service.js +215 -15
  145. package/src/services/uiComponentsAi.service.js +6 -19
  146. package/src/services/workflow.service.js +23 -8
  147. package/src/utils/orgRoles.js +14 -0
  148. package/src/utils/rbac/engine.js +60 -0
  149. package/src/utils/rbac/rightsRegistry.js +33 -0
  150. package/views/admin-blog-automation.ejs +877 -0
  151. package/views/admin-blog-edit.ejs +542 -0
  152. package/views/admin-blog.ejs +399 -0
  153. package/views/admin-cache.ejs +681 -0
  154. package/views/admin-console-manager.ejs +680 -0
  155. package/views/admin-crons.ejs +645 -0
  156. package/views/admin-dashboard.ejs +28 -8
  157. package/views/admin-db-browser.ejs +445 -0
  158. package/views/admin-ejs-virtual.ejs +16 -10
  159. package/views/admin-experiments.ejs +91 -0
  160. package/views/admin-file-manager.ejs +942 -0
  161. package/views/admin-health-checks.ejs +725 -0
  162. package/views/admin-i18n.ejs +59 -5
  163. package/views/admin-llm.ejs +99 -1
  164. package/views/admin-organizations.ejs +163 -1
  165. package/views/admin-pages.ejs +2424 -0
  166. package/views/admin-proxy.ejs +491 -0
  167. package/views/admin-rate-limiter.ejs +625 -0
  168. package/views/admin-rbac.ejs +1331 -0
  169. package/views/admin-scripts.ejs +597 -3
  170. package/views/admin-seo-config.ejs +61 -7
  171. package/views/admin-ui-components.ejs +57 -25
  172. package/views/admin-workflows.ejs +7 -7
  173. package/views/file-manager.ejs +866 -0
  174. package/views/pages/blocks/contact.ejs +27 -0
  175. package/views/pages/blocks/cta.ejs +18 -0
  176. package/views/pages/blocks/faq.ejs +20 -0
  177. package/views/pages/blocks/features.ejs +19 -0
  178. package/views/pages/blocks/hero.ejs +13 -0
  179. package/views/pages/blocks/html.ejs +5 -0
  180. package/views/pages/blocks/image.ejs +14 -0
  181. package/views/pages/blocks/testimonials.ejs +26 -0
  182. package/views/pages/blocks/text.ejs +10 -0
  183. package/views/pages/layouts/default.ejs +51 -0
  184. package/views/pages/layouts/minimal.ejs +42 -0
  185. package/views/pages/layouts/sidebar.ejs +54 -0
  186. package/views/pages/partials/footer.ejs +13 -0
  187. package/views/pages/partials/header.ejs +12 -0
  188. package/views/pages/partials/sidebar.ejs +8 -0
  189. package/views/pages/runtime/page.ejs +10 -0
  190. package/views/pages/templates/article.ejs +20 -0
  191. package/views/pages/templates/default.ejs +12 -0
  192. package/views/pages/templates/landing.ejs +14 -0
  193. package/views/pages/templates/listing.ejs +15 -0
  194. package/views/partials/admin-image-upload-modal.ejs +221 -0
  195. package/views/partials/dashboard/nav-items.ejs +12 -0
  196. package/views/partials/dashboard/palette.ejs +5 -3
  197. package/views/partials/llm-provider-model-picker.ejs +183 -0
  198. package/src/routes/llmUi.routes.js +0 -26
@@ -0,0 +1,109 @@
1
+ const GlobalSetting = require('../models/GlobalSetting');
2
+ const ScriptDefinition = require('../models/ScriptDefinition');
3
+ const CronJob = require('../models/CronJob');
4
+
5
+ const globalSettingsService = require('./globalSettings.service');
6
+
7
+ const PUBLIC_STATUS_SETTING_KEY = 'healthChecks.publicStatusEnabled';
8
+
9
+ const CLEANUP_SCRIPT_CODE_IDENTIFIER = 'health-checks-cleanup-history';
10
+ const CLEANUP_CRON_NAME = 'Health Checks: Cleanup run history';
11
+
12
+ function cleanupScriptSource() {
13
+ // This script runs out-of-process (host runner). It connects to Mongo and performs cleanup.
14
+ return `
15
+ (async () => {
16
+ const mongoose = require('mongoose');
17
+
18
+ const uri = process.env.MONGODB_URI || process.env.MONGO_URI;
19
+ if (!uri) {
20
+ console.error('MONGODB_URI (or MONGO_URI) is required');
21
+ process.exit(1);
22
+ }
23
+
24
+ const days = Number(process.env.HEALTHCHECKS_RETENTION_DAYS || 30);
25
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
26
+
27
+ await mongoose.connect(uri, { serverSelectionTimeoutMS: 5000, maxPoolSize: 2 });
28
+
29
+ const res = await mongoose.connection.db
30
+ .collection('health_check_runs')
31
+ .deleteMany({ startedAt: { $lt: cutoff } });
32
+
33
+ console.log('Deleted', res.deletedCount || 0, 'health_check_runs older than', days, 'days');
34
+
35
+ await mongoose.disconnect();
36
+ })().catch((err) => {
37
+ console.error(err);
38
+ process.exit(1);
39
+ });
40
+ `.trim();
41
+ }
42
+
43
+ async function ensurePublicStatusSettingExists() {
44
+ const existing = await GlobalSetting.findOne({ key: PUBLIC_STATUS_SETTING_KEY }).lean();
45
+ if (existing) return;
46
+
47
+ await GlobalSetting.create({
48
+ key: PUBLIC_STATUS_SETTING_KEY,
49
+ value: 'false',
50
+ type: 'boolean',
51
+ description: 'Enable the public health checks status summary endpoint (/api/health-checks/status).',
52
+ templateVariables: [],
53
+ public: false,
54
+ });
55
+
56
+ globalSettingsService.clearSettingsCache();
57
+ }
58
+
59
+ async function ensureCleanupScriptExists() {
60
+ let script = await ScriptDefinition.findOne({ codeIdentifier: CLEANUP_SCRIPT_CODE_IDENTIFIER });
61
+ if (script) return script;
62
+
63
+ script = await ScriptDefinition.create({
64
+ name: 'Health Checks: Cleanup run history (> 30 days)',
65
+ codeIdentifier: CLEANUP_SCRIPT_CODE_IDENTIFIER,
66
+ description: 'Deletes HealthCheckRun documents older than 30 days (configurable via HEALTHCHECKS_RETENTION_DAYS env var).',
67
+ type: 'node',
68
+ runner: 'host',
69
+ script: cleanupScriptSource(),
70
+ defaultWorkingDirectory: '',
71
+ env: [],
72
+ timeoutMs: 5 * 60 * 1000,
73
+ enabled: true,
74
+ });
75
+
76
+ return script;
77
+ }
78
+
79
+ async function ensureCleanupCronExists(scriptId) {
80
+ const existing = await CronJob.findOne({ name: CLEANUP_CRON_NAME, taskType: 'script' }).lean();
81
+ if (existing) return existing;
82
+
83
+ const cron = await CronJob.create({
84
+ name: CLEANUP_CRON_NAME,
85
+ description: 'Weekly cleanup of health check run history (disabled by default).',
86
+ cronExpression: '0 3 * * 0',
87
+ timezone: 'UTC',
88
+ enabled: false,
89
+ nextRunAt: null,
90
+ taskType: 'script',
91
+ scriptId,
92
+ scriptEnv: [],
93
+ timeoutMs: 5 * 60 * 1000,
94
+ createdBy: 'system',
95
+ });
96
+
97
+ return cron.toObject();
98
+ }
99
+
100
+ async function bootstrap() {
101
+ await ensurePublicStatusSettingExists();
102
+ const script = await ensureCleanupScriptExists();
103
+ await ensureCleanupCronExists(script._id);
104
+ }
105
+
106
+ module.exports = {
107
+ bootstrap,
108
+ PUBLIC_STATUS_SETTING_KEY,
109
+ };
@@ -0,0 +1,106 @@
1
+ const cron = require('node-cron');
2
+ const parser = require('cron-parser');
3
+
4
+ const HealthCheck = require('../models/HealthCheck');
5
+
6
+ const healthChecksService = require('./healthChecks.service');
7
+
8
+ class HealthChecksScheduler {
9
+ constructor() {
10
+ this.scheduledChecks = new Map(); // Map<checkId, cron.ScheduledTask>
11
+ this.isRunning = false;
12
+ }
13
+
14
+ async start() {
15
+ if (this.isRunning) return;
16
+ this.isRunning = true;
17
+
18
+ const enabled = await HealthCheck.find({ enabled: true }).lean();
19
+ for (const check of enabled) {
20
+ try {
21
+ // eslint-disable-next-line no-await-in-loop
22
+ await this.scheduleCheck(check);
23
+ } catch (err) {
24
+ console.error(`Failed to schedule health check ${check.name}:`, err);
25
+ }
26
+ }
27
+
28
+ console.log(`Health checks scheduler started with ${enabled.length} checks`);
29
+ }
30
+
31
+ async stop() {
32
+ for (const [, task] of this.scheduledChecks) {
33
+ task.stop();
34
+ }
35
+ this.scheduledChecks.clear();
36
+ this.isRunning = false;
37
+ console.log('Health checks scheduler stopped');
38
+ }
39
+
40
+ calculateNextRun(cronExpression, timezone = 'UTC') {
41
+ try {
42
+ const interval = parser.parseExpression(String(cronExpression || '').trim(), { tz: String(timezone || 'UTC') });
43
+ return interval.next().toDate();
44
+ } catch (err) {
45
+ console.error('Failed to calculate next run for health check:', err);
46
+ return null;
47
+ }
48
+ }
49
+
50
+ async scheduleCheck(healthCheck) {
51
+ const checkId = String(healthCheck._id);
52
+
53
+ if (this.scheduledChecks.has(checkId)) {
54
+ await this.unscheduleCheck(checkId);
55
+ }
56
+
57
+ if (!cron.validate(healthCheck.cronExpression)) {
58
+ throw new Error(`Invalid cron expression: ${healthCheck.cronExpression}`);
59
+ }
60
+
61
+ const task = cron.schedule(
62
+ healthCheck.cronExpression,
63
+ async () => {
64
+ try {
65
+ await healthChecksService.runHealthCheckOnce(checkId, { trigger: 'schedule' });
66
+ } catch (err) {
67
+ console.error(`Health check run failed (${healthCheck.name}):`, err);
68
+ }
69
+ },
70
+ {
71
+ scheduled: false,
72
+ timezone: healthCheck.timezone || 'UTC',
73
+ },
74
+ );
75
+
76
+ task.start();
77
+ this.scheduledChecks.set(checkId, task);
78
+
79
+ const nextRunAt = this.calculateNextRun(healthCheck.cronExpression, healthCheck.timezone);
80
+ await HealthCheck.updateOne({ _id: checkId }, { $set: { nextRunAt } });
81
+
82
+ console.log(`Scheduled health check: ${healthCheck.name} (${healthCheck.cronExpression})`);
83
+ }
84
+
85
+ async unscheduleCheck(checkId) {
86
+ const task = this.scheduledChecks.get(String(checkId));
87
+ if (!task) return;
88
+
89
+ task.stop();
90
+ this.scheduledChecks.delete(String(checkId));
91
+
92
+ await HealthCheck.updateOne({ _id: checkId }, { $set: { nextRunAt: null } });
93
+ }
94
+
95
+ async trigger(checkId) {
96
+ return await healthChecksService.runHealthCheckOnce(checkId, { trigger: 'manual' });
97
+ }
98
+
99
+ getScheduledChecks() {
100
+ return Array.from(this.scheduledChecks.keys());
101
+ }
102
+ }
103
+
104
+ const scheduler = new HealthChecksScheduler();
105
+
106
+ module.exports = scheduler;
@@ -162,10 +162,10 @@ async function createJsonConfig({ title, jsonRaw, publicEnabled = false, cacheTt
162
162
  jsonHash: computeJsonHash(String(jsonRaw)),
163
163
  };
164
164
 
165
- console.log('Creating document with data:', createData);
165
+ //console.log('Creating document with data:', createData);
166
166
 
167
167
  const doc = await JsonConfig.create(createData);
168
- console.log('Created document:', doc.toObject());
168
+ //console.log('Created document:', doc.toObject());
169
169
 
170
170
  clearJsonConfigCache(slug);
171
171
  if (normalizedAlias) {
@@ -0,0 +1,190 @@
1
+ const { getSettingValue } = require('./globalSettings.service');
2
+
3
+ const GLOBAL_DEFAULT_PROVIDER_KEY = 'llm.defaults.providerKey';
4
+ const GLOBAL_DEFAULT_MODEL_KEY = 'llm.defaults.model';
5
+ const SYSTEM_DEFAULTS_PREFIX = 'llm.systemDefaults.';
6
+ const PROVIDER_MODELS_KEY = 'llm.providerModels';
7
+
8
+ const CACHE_TTL = 60_000;
9
+ let cache = {
10
+ ts: 0,
11
+ globalDefaults: null,
12
+ systemDefaults: null,
13
+ providerModels: null,
14
+ };
15
+
16
+ function trimOrEmpty(value) {
17
+ return String(value || '').trim();
18
+ }
19
+
20
+ function parseJsonOrDefault(raw, fallback) {
21
+ if (!raw) return fallback;
22
+ try {
23
+ const parsed = JSON.parse(String(raw));
24
+ return parsed ?? fallback;
25
+ } catch (_) {
26
+ return fallback;
27
+ }
28
+ }
29
+
30
+ async function loadCentralConfig() {
31
+ const now = Date.now();
32
+ if (cache.ts && now - cache.ts < CACHE_TTL && cache.globalDefaults && cache.systemDefaults && cache.providerModels) {
33
+ return cache;
34
+ }
35
+
36
+ const providerKey = trimOrEmpty(await getSettingValue(GLOBAL_DEFAULT_PROVIDER_KEY, ''));
37
+ const model = trimOrEmpty(await getSettingValue(GLOBAL_DEFAULT_MODEL_KEY, ''));
38
+
39
+ const systemDefaultsRaw = await getSettingValue('llm.systemDefaults', '{}');
40
+ const systemDefaults = parseJsonOrDefault(systemDefaultsRaw, {});
41
+
42
+ const providerModelsRaw = await getSettingValue(PROVIDER_MODELS_KEY, '{}');
43
+ const providerModels = parseJsonOrDefault(providerModelsRaw, {});
44
+
45
+ cache = {
46
+ ts: now,
47
+ globalDefaults: { providerKey, model },
48
+ systemDefaults: systemDefaults && typeof systemDefaults === 'object' ? systemDefaults : {},
49
+ providerModels: providerModels && typeof providerModels === 'object' ? providerModels : {},
50
+ };
51
+
52
+ return cache;
53
+ }
54
+
55
+ function getLegacyResolver(systemKey) {
56
+ const key = String(systemKey || '').trim();
57
+
58
+ const map = {
59
+ 'pageBuilder.blocks.generate': {
60
+ legacyProviderSettingKey: 'pageBuilder.blocks.ai.providerKey',
61
+ legacyModelSettingKey: 'pageBuilder.blocks.ai.model',
62
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
63
+ envModelKey: 'DEFAULT_LLM_MODEL',
64
+ hardDefaultModel: 'x-ai/grok-code-fast-1',
65
+ },
66
+ 'pageBuilder.blocks.propose': {
67
+ legacyProviderSettingKey: 'pageBuilder.blocks.ai.providerKey',
68
+ legacyModelSettingKey: 'pageBuilder.blocks.ai.model',
69
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
70
+ envModelKey: 'DEFAULT_LLM_MODEL',
71
+ hardDefaultModel: 'x-ai/grok-code-fast-1',
72
+ },
73
+ 'ejsVirtual.vibe.apply': {
74
+ legacyProviderSettingKey: 'ejsVirtual.ai.providerKey',
75
+ legacyModelSettingKey: 'ejsVirtual.ai.model',
76
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
77
+ envModelKey: 'DEFAULT_LLM_MODEL',
78
+ hardDefaultModel: 'x-ai/grok-code-fast-1',
79
+ },
80
+ 'uiComponents.proposeEdit': {
81
+ legacyProviderSettingKey: 'uiComponents.ai.providerKey',
82
+ legacyModelSettingKey: 'uiComponents.ai.model',
83
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
84
+ envModelKey: 'DEFAULT_LLM_MODEL',
85
+ hardDefaultModel: 'x-ai/grok-code-fast-1',
86
+ },
87
+ 'headless.modelBuilder.chat': {
88
+ legacyProviderSettingKey: 'headless.aiProviderKey',
89
+ legacyModelSettingKey: 'headless.aiModel',
90
+ envProviderKey: 'HEADLESS_AI_PROVIDER_KEY',
91
+ envModelKey: 'HEADLESS_AI_MODEL',
92
+ hardDefaultProviderKey: 'openrouter',
93
+ hardDefaultModel: 'google/gemini-2.5-flash-lite',
94
+ },
95
+ 'workflow.node.llm': {
96
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
97
+ envModelKey: 'DEFAULT_LLM_MODEL',
98
+ hardDefaultProviderKey: 'openrouter',
99
+ hardDefaultModel: 'minimax/minimax-m2.1',
100
+ },
101
+ 'seoConfig.entry.generate': {
102
+ legacyModelSettingKey: 'seoconfig.ai.openrouter.model',
103
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
104
+ envModelKey: 'DEFAULT_LLM_MODEL',
105
+ hardDefaultProviderKey: 'openrouter',
106
+ },
107
+ 'seoConfig.entry.improve': {
108
+ legacyModelSettingKey: 'seoconfig.ai.openrouter.model',
109
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
110
+ envModelKey: 'DEFAULT_LLM_MODEL',
111
+ hardDefaultProviderKey: 'openrouter',
112
+ },
113
+ 'seoConfig.ogSvg.edit': {
114
+ legacyModelSettingKey: 'seoconfig.ai.openrouter.model',
115
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
116
+ envModelKey: 'DEFAULT_LLM_MODEL',
117
+ hardDefaultProviderKey: 'openrouter',
118
+ },
119
+ 'i18n.translate.preview': {
120
+ legacyModelSettingKey: 'i18n.ai.model',
121
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
122
+ envModelKey: 'DEFAULT_LLM_MODEL',
123
+ hardDefaultProviderKey: 'openrouter',
124
+ hardDefaultModel: 'google/gemini-2.5-flash-lite',
125
+ },
126
+ 'i18n.translate.text': {
127
+ legacyModelSettingKey: 'i18n.ai.model',
128
+ envProviderKey: 'DEFAULT_LLM_PROVIDER_KEY',
129
+ envModelKey: 'DEFAULT_LLM_MODEL',
130
+ hardDefaultProviderKey: 'openrouter',
131
+ hardDefaultModel: 'google/gemini-2.5-flash-lite',
132
+ },
133
+ };
134
+
135
+ return map[key] || null;
136
+ }
137
+
138
+ async function resolveLlmProviderModel({ systemKey, providerKey, model }) {
139
+ const uiProviderKey = trimOrEmpty(providerKey);
140
+ const uiModel = trimOrEmpty(model);
141
+
142
+ const { globalDefaults, systemDefaults } = await loadCentralConfig();
143
+
144
+ const perSystem = systemKey && systemDefaults && typeof systemDefaults === 'object'
145
+ ? systemDefaults[String(systemKey)]
146
+ : null;
147
+
148
+ const systemProviderKey = trimOrEmpty(perSystem && perSystem.providerKey);
149
+ const systemModel = trimOrEmpty(perSystem && perSystem.model);
150
+
151
+ const globalProviderKey = trimOrEmpty(globalDefaults && globalDefaults.providerKey);
152
+ const globalModel = trimOrEmpty(globalDefaults && globalDefaults.model);
153
+
154
+ const legacy = getLegacyResolver(systemKey);
155
+
156
+ const legacyProviderKey = legacy?.legacyProviderSettingKey
157
+ ? trimOrEmpty(await getSettingValue(legacy.legacyProviderSettingKey, ''))
158
+ : '';
159
+ const legacyModel = legacy?.legacyModelSettingKey
160
+ ? trimOrEmpty(await getSettingValue(legacy.legacyModelSettingKey, ''))
161
+ : '';
162
+
163
+ const envProviderKey = legacy?.envProviderKey ? trimOrEmpty(process.env[legacy.envProviderKey]) : '';
164
+ const envModel = legacy?.envModelKey ? trimOrEmpty(process.env[legacy.envModelKey]) : '';
165
+
166
+ const resolvedProviderKey = uiProviderKey || systemProviderKey || globalProviderKey || legacyProviderKey || envProviderKey || trimOrEmpty(legacy?.hardDefaultProviderKey);
167
+ if (!resolvedProviderKey) {
168
+ const err = new Error('Missing LLM providerKey');
169
+ err.code = 'VALIDATION';
170
+ throw err;
171
+ }
172
+
173
+ const resolvedModel = uiModel || systemModel || globalModel || legacyModel || envModel || trimOrEmpty(legacy?.hardDefaultModel);
174
+
175
+ return { providerKey: resolvedProviderKey, model: resolvedModel };
176
+ }
177
+
178
+ async function getProviderModelsMap() {
179
+ const { providerModels } = await loadCentralConfig();
180
+ return providerModels && typeof providerModels === 'object' ? providerModels : {};
181
+ }
182
+
183
+ module.exports = {
184
+ GLOBAL_DEFAULT_PROVIDER_KEY,
185
+ GLOBAL_DEFAULT_MODEL_KEY,
186
+ SYSTEM_DEFAULTS_PREFIX,
187
+ PROVIDER_MODELS_KEY,
188
+ resolveLlmProviderModel,
189
+ getProviderModelsMap,
190
+ };
@@ -1,3 +1,5 @@
1
+ const { S3Client, HeadBucketCommand, CreateBucketCommand, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
2
+
1
3
  async function createS3Endpoint({ endpoint, region, bucket, accessKeyId, secretAccessKey, forcePathStyle } = {}) {
2
4
  const safeEndpoint = String(endpoint || '').trim();
3
5
  const safeBucket = String(bucket || '').trim();
@@ -10,8 +12,6 @@ async function createS3Endpoint({ endpoint, region, bucket, accessKeyId, secretA
10
12
  throw err;
11
13
  }
12
14
 
13
- const { S3Client, HeadBucketCommand, CreateBucketCommand, PutObjectCommand, GetObjectCommand } = await import('@aws-sdk/client-s3');
14
-
15
15
  const client = new S3Client({
16
16
  endpoint: safeEndpoint,
17
17
  region: String(region || 'us-east-1').trim() || 'us-east-1',