@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,54 @@
1
+ const {
2
+ generateContextBlock,
3
+ proposeContextBlockEdit,
4
+ } = require('../services/pagesContextBlocksAi.service');
5
+
6
+ const { getBasicAuthActor } = require('../services/audit.service');
7
+
8
+ function handleError(res, err) {
9
+ const code = err && err.code;
10
+ if (code === 'VALIDATION') return res.status(400).json({ error: err.message });
11
+ if (code === 'NOT_FOUND') return res.status(404).json({ error: err.message });
12
+ if (code === 'AI_INVALID') return res.status(500).json({ error: err.message });
13
+ return res.status(500).json({ error: err.message || 'Operation failed' });
14
+ }
15
+
16
+ exports.generate = async (req, res) => {
17
+ try {
18
+ const actor = getBasicAuthActor(req);
19
+ const { prompt, blockType, providerKey, model } = req.body || {};
20
+
21
+ const result = await generateContextBlock({
22
+ prompt,
23
+ blockType,
24
+ providerKey,
25
+ model,
26
+ actor,
27
+ });
28
+
29
+ return res.json(result);
30
+ } catch (err) {
31
+ console.error('[adminPagesContextBlocksAi] generate error', err);
32
+ return handleError(res, err);
33
+ }
34
+ };
35
+
36
+ exports.propose = async (req, res) => {
37
+ try {
38
+ const actor = getBasicAuthActor(req);
39
+ const { prompt, currentBlock, providerKey, model } = req.body || {};
40
+
41
+ const result = await proposeContextBlockEdit({
42
+ prompt,
43
+ currentBlock,
44
+ providerKey,
45
+ model,
46
+ actor,
47
+ });
48
+
49
+ return res.json(result);
50
+ } catch (err) {
51
+ console.error('[adminPagesContextBlocksAi] propose error', err);
52
+ return handleError(res, err);
53
+ }
54
+ };
@@ -0,0 +1,113 @@
1
+ const ProxyEntry = require('../models/ProxyEntry');
2
+ const { listDiscoveries } = require('../services/proxy.service');
3
+
4
+ function handleServiceError(res, error) {
5
+ const msg = error?.message || 'Operation failed';
6
+ const code = error?.code;
7
+
8
+ if (code === 'VALIDATION') return res.status(400).json({ error: msg });
9
+ if (code === 'NOT_FOUND') return res.status(404).json({ error: msg });
10
+ if (code === 'CONFLICT') return res.status(409).json({ error: msg });
11
+
12
+ return res.status(500).json({ error: msg });
13
+ }
14
+
15
+ exports.list = async (req, res) => {
16
+ try {
17
+ const [entries, discoveries] = await Promise.all([
18
+ ProxyEntry.find({}).sort({ updatedAt: -1 }).lean(),
19
+ listDiscoveries(),
20
+ ]);
21
+
22
+ return res.json({ items: entries || [], discoveries: discoveries || [] });
23
+ } catch (error) {
24
+ console.error('Error listing proxy entries:', error);
25
+ return handleServiceError(res, error);
26
+ }
27
+ };
28
+
29
+ exports.get = async (req, res) => {
30
+ try {
31
+ const id = String(req.params.id || '').trim();
32
+ const item = await ProxyEntry.findById(id).lean();
33
+ if (!item) return res.status(404).json({ error: 'Not found' });
34
+ return res.json({ item });
35
+ } catch (error) {
36
+ console.error('Error getting proxy entry:', error);
37
+ return handleServiceError(res, error);
38
+ }
39
+ };
40
+
41
+ exports.create = async (req, res) => {
42
+ try {
43
+ const body = req.body && typeof req.body === 'object' ? req.body : {};
44
+
45
+ const matchValue = String(body.match?.value || '').trim();
46
+ if (!matchValue) return res.status(400).json({ error: 'match.value is required' });
47
+
48
+ const doc = await ProxyEntry.create({
49
+ name: String(body.name || ''),
50
+ enabled: Boolean(body.enabled),
51
+ match: {
52
+ type: String(body.match?.type || 'contains'),
53
+ value: matchValue,
54
+ applyTo: String(body.match?.applyTo || 'host'),
55
+ flags: String(body.match?.flags || 'i'),
56
+ },
57
+ policy: body.policy || undefined,
58
+ rateLimit: body.rateLimit || undefined,
59
+ cache: body.cache || undefined,
60
+ headers: body.headers || undefined,
61
+ transform: body.transform || undefined,
62
+ });
63
+
64
+ return res.status(201).json({ item: doc.toObject() });
65
+ } catch (error) {
66
+ console.error('Error creating proxy entry:', error);
67
+ return handleServiceError(res, error);
68
+ }
69
+ };
70
+
71
+ exports.update = async (req, res) => {
72
+ try {
73
+ const id = String(req.params.id || '').trim();
74
+ const updates = req.body && typeof req.body === 'object' ? req.body : {};
75
+
76
+ const doc = await ProxyEntry.findById(id);
77
+ if (!doc) return res.status(404).json({ error: 'Not found' });
78
+
79
+ if (updates.name !== undefined) doc.name = String(updates.name || '');
80
+ if (updates.enabled !== undefined) doc.enabled = Boolean(updates.enabled);
81
+
82
+ if (updates.match && typeof updates.match === 'object') {
83
+ if (updates.match.type !== undefined) doc.match.type = String(updates.match.type || 'contains');
84
+ if (updates.match.value !== undefined) doc.match.value = String(updates.match.value || '').trim();
85
+ if (updates.match.applyTo !== undefined) doc.match.applyTo = String(updates.match.applyTo || 'host');
86
+ if (updates.match.flags !== undefined) doc.match.flags = String(updates.match.flags || 'i');
87
+ }
88
+
89
+ if (updates.policy !== undefined) doc.policy = updates.policy;
90
+ if (updates.rateLimit !== undefined) doc.rateLimit = updates.rateLimit;
91
+ if (updates.cache !== undefined) doc.cache = updates.cache;
92
+ if (updates.headers !== undefined) doc.headers = updates.headers;
93
+ if (updates.transform !== undefined) doc.transform = updates.transform;
94
+
95
+ await doc.save();
96
+ return res.json({ item: doc.toObject() });
97
+ } catch (error) {
98
+ console.error('Error updating proxy entry:', error);
99
+ return handleServiceError(res, error);
100
+ }
101
+ };
102
+
103
+ exports.delete = async (req, res) => {
104
+ try {
105
+ const id = String(req.params.id || '').trim();
106
+ const doc = await ProxyEntry.findByIdAndDelete(id).lean();
107
+ if (!doc) return res.status(404).json({ error: 'Not found' });
108
+ return res.json({ ok: true });
109
+ } catch (error) {
110
+ console.error('Error deleting proxy entry:', error);
111
+ return handleServiceError(res, error);
112
+ }
113
+ };
@@ -0,0 +1,138 @@
1
+ const rateLimiter = require('../services/rateLimiter.service');
2
+
3
+ function handleServiceError(res, error) {
4
+ const msg = error?.message || 'Operation failed';
5
+ const code = error?.code;
6
+
7
+ if (code === 'VALIDATION' || code === 'INVALID_JSON') {
8
+ return res.status(400).json({ error: msg });
9
+ }
10
+ if (code === 'NOT_FOUND') {
11
+ return res.status(404).json({ error: msg });
12
+ }
13
+
14
+ return res.status(500).json({ error: msg });
15
+ }
16
+
17
+ exports.list = async (req, res) => {
18
+ try {
19
+ const items = await rateLimiter.list();
20
+ return res.json({ items });
21
+ } catch (error) {
22
+ console.error('Error listing rate limits:', error);
23
+ return handleServiceError(res, error);
24
+ }
25
+ };
26
+
27
+ exports.getConfig = async (req, res) => {
28
+ try {
29
+ const { doc } = await rateLimiter.getRateLimitsConfigData();
30
+ return res.json({
31
+ config: {
32
+ id: String(doc._id),
33
+ slug: doc.slug,
34
+ alias: doc.alias,
35
+ title: doc.title,
36
+ cacheTtlSeconds: Number(doc.cacheTtlSeconds || 0) || 0,
37
+ jsonRaw: String(doc.jsonRaw || ''),
38
+ updatedAt: doc.updatedAt,
39
+ },
40
+ });
41
+ } catch (error) {
42
+ console.error('Error getting rate limits config:', error);
43
+ return handleServiceError(res, error);
44
+ }
45
+ };
46
+
47
+ exports.updateConfig = async (req, res) => {
48
+ try {
49
+ const jsonRaw = req.body?.jsonRaw;
50
+ if (jsonRaw === undefined || jsonRaw === null) {
51
+ return res.status(400).json({ error: 'jsonRaw is required' });
52
+ }
53
+
54
+ const doc = await rateLimiter.updateRawConfig({ jsonRaw });
55
+ return res.json({
56
+ config: {
57
+ id: String(doc._id),
58
+ slug: doc.slug,
59
+ alias: doc.alias,
60
+ title: doc.title,
61
+ cacheTtlSeconds: Number(doc.cacheTtlSeconds || 0) || 0,
62
+ jsonRaw: String(doc.jsonRaw || ''),
63
+ updatedAt: doc.updatedAt,
64
+ },
65
+ });
66
+ } catch (error) {
67
+ console.error('Error updating rate limits config:', error);
68
+ return handleServiceError(res, error);
69
+ }
70
+ };
71
+
72
+ exports.updateLimiter = async (req, res) => {
73
+ try {
74
+ const limiterId = String(req.params.id || '').trim();
75
+ if (!limiterId) return res.status(400).json({ error: 'id is required' });
76
+
77
+ let override = req.body?.override;
78
+ if (override === undefined) override = req.body;
79
+
80
+ if (!override || typeof override !== 'object' || Array.isArray(override)) {
81
+ return res.status(400).json({ error: 'override must be an object' });
82
+ }
83
+
84
+ const config = await rateLimiter.setLimiterOverride(limiterId, override);
85
+ return res.json({ config });
86
+ } catch (error) {
87
+ console.error('Error updating rate limit override:', error);
88
+ return handleServiceError(res, error);
89
+ }
90
+ };
91
+
92
+ exports.resetLimiter = async (req, res) => {
93
+ try {
94
+ const limiterId = String(req.params.id || '').trim();
95
+ if (!limiterId) return res.status(400).json({ error: 'id is required' });
96
+
97
+ const config = await rateLimiter.resetLimiterOverride(limiterId);
98
+ return res.json({ config });
99
+ } catch (error) {
100
+ console.error('Error resetting rate limit override:', error);
101
+ return handleServiceError(res, error);
102
+ }
103
+ };
104
+
105
+ exports.getMetrics = async (req, res) => {
106
+ try {
107
+ const start = req.query?.start;
108
+ const end = req.query?.end;
109
+
110
+ const data = await rateLimiter.queryMetrics({ start, end });
111
+ return res.json(data);
112
+ } catch (error) {
113
+ console.error('Error fetching rate limits metrics:', error);
114
+ return handleServiceError(res, error);
115
+ }
116
+ };
117
+
118
+ exports.bulkEnabled = async (req, res) => {
119
+ try {
120
+ const enabled = req.body?.enabled;
121
+ const all = Boolean(req.body?.all);
122
+ const ids = req.body?.ids;
123
+
124
+ if (enabled !== true && enabled !== false) {
125
+ return res.status(400).json({ error: 'enabled must be true or false' });
126
+ }
127
+
128
+ if (!all && !Array.isArray(ids)) {
129
+ return res.status(400).json({ error: 'Provide ids[] or set all=true' });
130
+ }
131
+
132
+ const config = await rateLimiter.bulkSetEnabled({ enabled, all, ids });
133
+ return res.json({ config });
134
+ } catch (error) {
135
+ console.error('Error bulk updating rate limits enabled state:', error);
136
+ return handleServiceError(res, error);
137
+ }
138
+ };